需要先明确一点是,内存模型中涉及到的地址都是32位系统下的,其中涉及到的内存都是虚拟内存和虚拟地址,之所以会出现虚拟,是系统为了能更好、更高效管理使用内存,对实际物理内存进行了映射,使得应用程序的执行不用考虑物理内存的硬件细节。
(1)因为是按照“先进后出”原则来操作的,此为栈。系统会自动申请栈内存并判断,空间不够的话会提示栈溢出。
(2)栈的全称是“运行时栈”,在程序运行过程中每当有一个函数被调用,会从栈中分配一帧内存,来存放局部变量,包括所有的形参,当函数调用结束,该内存就会立即被释放掉。
(3)栈也能实现函数的嵌套调用与返回,可以保存切换函数时的当前代码地址和寄存器值,此过程便是“保存现场”,被嵌套的函数执行完毕后,程序从之前保存的代码地址接着往下运行,称为“恢复现场”。
(4)栈的大小有限,不同系统栈的大小各有不同,如果嵌套函数过多,占用栈内存不断增大,超过最大值可能会产生“栈溢出”导致程序奔溃。
(1)堆内存经常被称为自由区,因为这个区域内的大小定义和释放量都是由用户自己决定。
(2)堆的全称为“运行时堆”,因为随着进程的运行,堆的大小都在改变。
(3)堆内存的生命周期从malloc()/calloc()/realloc()开始,到free()/delete()结束,申请内存与释放内存的时机可以由用户自定义。如果结束时用户不释放,则有可能由系统进行回收。
(4)堆是没有大小限制的,最大值由物理内存决定,唯一限制的是虚拟内存的大小,当虚拟内存的内存池用完的时候,系统会从磁盘开始切割内存出来用,但是切割出来的内存速度很慢,这个切割的大小可以在系统的内存设置中配置。
数据段分为.bss、.data、.rodata三个区域,结束时由系统进行释放
(1).bss
存放还未初始化的静态变量(所有全局变量以及static局部变量),并统一初始化为0。
(2).data
存放已经初始化的静态变量
(3).rodata
存放只读变量,也就是常量。
(1).test
存放用户定义的所有函数代码。
(2).init
存放系统给每一个可执行程序自动添加的初始化代码,该代码只执行一遍,包括环境变量的配置、命令行参数组织和传递等,这部分数据则被放在了栈底,即紧挨着内核的地方。
如果环境变量被修改,修改后的新的环境变量会被复制到堆中。
如果用户自定义的函数只需要执行一遍的话,也可以挡在.init段中。
这么做的目的是为了节省磁盘空间,因为还未初始化的静态变量都被统一赋值成了0,所以就不用再专门开辟新的内存去存放变量的值了。
(1)内核大小:0xC000 0000~0xffff fffff =====>0x3FFF FFFF ========>1073741823Byte=========>1G;
(2)在32位系统上,理论上内存大小2^32 = 4G;
(3)因此理论上堆的最大申请空间为3G,可实际上少于3G,因为还有栈区,数据段和代码段;
频繁的申请和释放内存,必然导致一些没有被利用的碎片内存出现,这样造成的后果就是一旦需要一片连续内存的时候,即使内存总量足够,也无法满足连续的要求,这就是内存碎片问题。
用户申请使用了一块内存,用完却没有释放,那么这块内存就谁也用不了,这就是内存泄漏。
内存泄漏如果一直发生,那么内存被占用会越来越多,超过内存总大小就奔溃了,这就是内存溢出。