跟我学C++中级篇——进程内存空间的管理

一、C和C++内存区域划分

面试时经常遇到一个问题,进程内存空间有几部分?或者说程序运行时内存区域可分成几个部分。而且会进一步问C和C++程序的运行时内存区域划分有什么不同?做为一个C++程序员,懂一些这个其实是有好处的。如果要搞安全方向的开发,更需要对此理解比较深刻。一般来说C和C++会将进程的内存空间划分为以下几个部分:
1、代码段(.text)
这部分存放了程序的代码(机器码)。它在编译期已经确定,是只读的,毕竟正常情况下,运行的程序是不允许修改的(非正常的病毒和木马除外)。
2、全局/静态区——初始化数据区(.data)
Initialized Data Segment,包含了初始化的全局和静态数据,此部分为可读写的,其大小在编译时已经确定。其由系统默认进行管理,不用开发者手动管理。
3、全局/静态区——未初始化数据段(BSS段)
Uninitialized Data Segment,包含了未初始化的全局变量和静态变量。这部分内存在程序启动时被初始化为零,但不包含任何初始值。注意,有些情况下,BSS和.data可能合在一起。一般来说bss段不占用磁盘空间大小(仅记录内存大小)。BSS段消除了未定义行为,确保了程序的安全性,提高了初始化的速度(批量置零)。其由系统默认进行管理,不用开发者手动管理。
4、堆(Heap):
应用程序运行时动态分析内在的区域,是C++中灵活分配内存的地方。不同的OS有不同的堆管理方式,这个可参看相关的书籍和资料。C++可以通过new和delete对内存分配释放进行管理(即指针变量)。另外,堆的大小是可以动态调整的。堆的生长方向是自低向高的。
堆分配的内存空间较大,但分配和回收的速度都比较慢并且需要开发者自己手动的进行管理。所以在C++新标准中,推荐使用智能指针进行内存的应用。
5、栈(Stack)
一般来说,函数的参数,局部变量以及返回值等,都会存储在栈空间上,栈是不需要开发者手动管理的,它是由系统自动管理的。栈的大小与OS有关,比如在Window平台上线程的栈默认是1M,而Linux中则默认为10M(也有8M,2M的请区分场景和具体的系统)。一般来说,虽然栈可调整(Linux使用ulimit -s进行调整,Windows上可以在开发工具如VS直接配置),但都不推荐调整栈的空间大小。一般来说栈的生长方向是自高向低的(当然也有反向生长的栈)。
既然栈有大小,在实际的开发中,要注意不要越界,比如实现递归算法时,就要防止栈空间的溢出。
6、字符串常量区(.rdata)
这个比较好理解,就是存储常量字符串等的区域。它也不需要开发者手动管理。

另外,在实际的应用中,大家可能会遇到有些环境变量和相关的参数(如程序启动时的命令行参数),它们通常也位于 BSS 段或数据段中。
一般来说,上面这六部分的分布顺序是代码段、常量区、全局静态区以及堆区和栈区,地址是由低向高的方向生长。
说明:大家可以使用一些反编译工具对上述区域进行显示的对比分析,如:

objdump -s -j .data runApp
Contents of section .data:
 5000 00000000 00000000 08500000 00000000  .........P......

二、二者有何不同

上面的划分是按照总体的情况来划分的,其实在一些细节上,C和C++的划分还是有着不同的。主要有:
1、C++标准中定义了“自由存储区(The free store is the dynamic memory pool from which memory is allocated by the new expression.)”,即由“new/delete”管理的内存区域,也就是上面提到的堆。在C++中,new/delete底层一般也是调用malloc/free来实现堆内存的管理,大家可以理解为,C++对堆内存的管理进行了进一步的抽象。而堆本身是底层接口(OS或库)的内存管理。自由存储区更倾向于C++中对象的生命周期的管理,尤其是对象的初始化和内存的分配/释放可以单独进行。
由于C++和兼容C的,所以C++中仍然有堆的概念。
2、可能早期的C标准与C++标准中对全局变量的初始化和未初始化部分有细节不同,但在新的标准(C99/C++99)后,这些都趋于了统一,大家在看到相关的一些资料如果提到这些需要注意

三、内存区域的作用

其实所有的管理的目标本质都是相同的,即更好的整合当前的资源,将相关的资源的利用最大化或最优化。同样,程序运行时的内存分区也是如此,它的目标就是尽量优化内存的使用并允许多个进程使用相同的代码和资源。而不同的空间的分配管理,也便于理解和优化代码。
也就是说,能够深入的理解并掌握程序在运行的布局控制,对于编写高效、安全的代码是非常有必要的。诸如大家常见的防止内存泄露和内存碎片以及耳熟能详的栈溢出的问题都可以进行合理的控制。
特别是在并行编程和现代的分布式编程中,不同的内存空间区域之间的控制和管理以及在不同的运行单元(线程、进程、核心、CPU甚至分布式的电脑等)中的内存资源的隔离和共享都有着重要的作用。
重点需要说明的是,C++和C的标准在不断的演进,而且速度有加快的趋势,所以上述的说明有可能在新标准中会不断的完善甚至变化,请大家不要拘泥于分区本身,更重要的是明白为什么要分区。分成每个区的目的,这样在新标准出现后,会更好的理解这些标准出现的原因。

四、总结

其实写程序和大家学习过的庄子的《庖丁解牛》有着异曲同工之妙。要想“恢恢乎其游刃必有余地”,则必须于整个程序运行的全部流程清晰明白。不能光知其然,更要知其所以然。只有知道程序如何进行内存分配、映射以及运行和调度的过程,才能更好的理解程序本身并写出更优秀的代码。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值