程序内存分布

    科技2025-02-18  9

    文章目录

    内存分区cpp动态分配和回收原则堆 堆区和栈区变量对比全局静态存储区和常量存储区的变量对比内存泄漏

    内存分区

    int a = 0; //(GVAR)全局初始化区 int* p1; //(bss)全局未初始化区 int main() //(text)代码区 { int b; //(static)栈区变量 char s[] = "abc"; //(static)栈区变量 int* p2 = NULL; //(static)栈区变量 char *p3 = "123456"; //p3在栈区,123456\0在常量区 static int c = 0; //(GVAR)全局(静态)初始化区,整个文件内部都可见 p1 = new int(10); //内部指向一个(heap)堆区变量 p2 = new int(20); //内部指向一个(heap)堆区变量 char* p4 = new char[7]; //内部指向一个(heap)堆区变量 strcpy_s(p4,7,"123456");//(text)代码区 return 0; //(text)代码区 }

    栈空间和堆空间的内存增长方向不一样。栈空间是从高地址向低地址增长的过程,堆空间是从低地址向高地址增长的过程。内存正常都是从低地址向高地址增长。

    栈空间中的变量定义好后,这个变量的内部存储、资源分配的操作完全交由编译器和系统来完成。堆区的变量由程序员动态的创建。当然我们new出空间后要记得delete,不然会造成内存泄漏。

    在堆区和常量区之间还有一个常量区,图中没有体现出来。

    常量区分为全局初始化区和全局未初始化区。

    cpp动态分配和回收原则

      堆是一种动态分配资源的方式,从现代编程语言角度看,动态分配内存是一件很自然的事情。动态分配内存可以由程序员来掌握,但是会带来一些不确定性:内存分配耗时需要多久?失败了怎么办?在实时性要求比较高的场合,比如一些嵌入式控制器和电信设备,这些情况就不容忽视,所以这些场合一般使用C++而不是java(java一般全是在堆中分配)。

      一般而言,但我们在堆上分配内存时,很多语言会使用new这样的关键字,有些语言则是隐式分配。在C++中new对应的是delete,这样可以让程序员完全接管内存的分配和释放。

    程序通常需要牵涉到三个内存管理器的操作,C++做了1、2两件事,java做了1、3两件事:

    分配一个某大小的内存块;释放一个之前分配的内存块;垃圾收集操作,寻找不再使用的内存块并予以释放。这个回收策略需要实现性能、实时性、额外开销等放面的平衡,很难有统一和高效的做法。

    堆区和栈区变量对比

    stackheap作用域函数体内,语句块{}作用域 内整个程序范围内,由new,malloc开始,delete、free结束编译间大小确定变量大小范围确定变量大小范围不确定。需要运行期间确定大小范围Windows系统默认栈大小是1M,linux默认大小是8M或10M,用ulinit -s查看。栈区很小,我们如果在栈区定义一个很大的变量是有风险的所有系统的堆空间上限是接近内存(虚拟内存)的总大小(一部分被OS占用)内存分配方式地址由高到低减少地址由低到高增加内容是否可变可变可变

    全局静态存储区和常量存储区的变量对比

    全局静态存储区常量存储区存储内容全局变量,静态变量常量编译期间大小是否确定确定确定内容是否可变可变不可变

    内存泄漏

      内存泄漏是指程序中已经动态分配的堆内存由于某种原因程序没有释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统程序崩溃等严重后果。

      内存泄漏发生原因和排查:

    内存泄漏主要发生在堆内存分配方式中,即“配置了内存后,所有指向该内存的指针都遗失了”。若缺乏垃圾回收机制,这样的内存就无法归还系统。因为内存泄漏属于程序运行中的问题,无法通过编译来识别,所以只能在程序运行过程中来判别和诊断。
    Processed: 0.010, SQL: 8