该区域用于映射可执行文件用到的动态链接库 。在Linux 2.4版本中,若可执行文件依赖共享库,则系统会为这些动态库在从0x40000000开始的地址分配相应空间,并在程序装载时将其载入到该空间 。在Linux 2.6内核中,共享库的起始地址被往上移动至更靠近栈区的位置 。
从进程地址空间的布局可以看到,在有共享库的情况下,留给堆的可用空间还有两处:一处是从.bss段到0x40000000,约不到1GB的空间;另一处是从共享库到栈之间的空间,约不到2GB 。这两块空间大小取决于栈、共享库的大小和数量 。这样来看,是否应用程序可申请的最大堆空间只有2GB?事实上,这与Linux内核版本有关 。在上面给出的进程地址空间经典布局图中,共享库的装载地址为0x40000000,这实际上是Linux kernel 2.6版本之前的情况了,在2.6版本里,共享库的装载地址已经被挪到靠近栈的位置,即位于0xBFxxxxxx附近,因此,此时的堆范围就不会被共享库分割成2个“碎片”,故kernel 2.6的32位Linux系统中,malloc申请的最大内存理论值在2.9GB左右 。
4 堆(heap)堆用于存放进程运行时动态分配的内存段,可动态扩张或缩减 。堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问 。当进程调用malloc(C)/new(C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free(C)/delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减)。
分配的堆内存是经过字节对齐的空间,以适合原子操作 。堆管理器通过链表管理每个申请的内存,由于堆申请和释放是无序的,最终会产生内存碎片 。堆内存一般由应用程序分配释放,回收的内存可供重新使用 。若程序员不释放,程序结束时操作系统可能会自动回收 。
堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆,一般由系统自动调用 。
使用堆时经常出现两种问题:1) 释放或改写仍在使用的内存(“内存破坏”);2)未释放不再使用的内存(“内存泄漏”) 。当释放次数少于申请次数时,可能已造成内存泄漏 。泄漏的内存往往比忘记释放的数据结构更大,因为所分配的内存通常会圆整为下个大于申请数量的2的幂次(如申请212B,会圆整为256B) 。
注意,堆不同于数据结构中的”堆”,其行为类似链表 。
【扩展阅读】栈和堆的区别
①管理方式:栈由编译器自动管理;堆由程序员控制,使用方便,但易产生内存泄露 。
②生长方向:栈向低地址扩展(即”向下生长”),是连续的内存区域;堆向高地址扩展(即”向上生长”),是不连续的内存区域 。这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历 。
③空间大小:栈顶地址和栈的最大容量由系统预先规定(通常默认2M或10M);堆的大小则受限于计算机系统中有效的虚拟内存,32位Linux系统中堆内存可达2.9G空间 。
④存储内容:栈在函数调用时,首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,然后是被调函数的局部变量 。本次调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行下条可执行语句 。堆通常在头部用一个字节存放其大小,堆用于存储生存期与函数调用无关的数据,具体内容由程序员安排 。
⑤分配方式:栈可静态分配或动态分配 。静态分配由编译器完成,如局部变量的分配 。动态分配由alloca函数在栈上申请空间,用完后自动释放 。堆只能动态分配且手工释放 。
⑥分配效率:栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行,因此效率较高 。堆由函数库提供,机制复杂,效率比栈低得多 。Windows系统中VirtualAlloc可直接在进程地址空间中分配一块内存,快速且灵活 。
⑦分配后系统响应:只要栈剩余空间大于所申请空间,系统将为程序提供内存,否则报告异常提示栈溢出 。
操作系统为堆维护一个记录空闲内存地址的链表 。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序 。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回 。,大多数系统会在该内存空间首地址处记录本次分配的内存大小,供后续的释放函数(如free/delete)正确释放本内存空间 。
推荐阅读
- 一文看懂“超融合”和“虚拟化”的区别
- Linux 升级gcc g++ gdb glibc教程
- 这27个常用Linux命令,我建议大家都熟练掌握,工作中够用了
- linux中的重定向
- linux如何设置主机名称
- linux内核设备树及编译
- CentOS继任者Rocky Linux 8.5新特性
- 小白黑客如何使用Kali Linux中间人获取内网计算机图片信息
- Intel|首次使用EUV工艺 Intel “4nm”酷睿点亮:成功运行Win/Linux/Chrome三大系统
- Linux 中 ss 命令的使用实例