C++堆内存分配

本文探讨了C++堆内存分配的抽象与分层,重点关注如何扩展和维护有效堆内存。通过`brk`和`sbrk`系统调用以及`operator new`,详细阐述了C++程序如何获取和管理堆内存,强调了内存管理在C++编程中的重要性,特别是异常处理和智能指针的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++堆内存分配

抽象与分层

​ c和c++的内存服务模型与计算机网络里面的协议分层模型有点类似。计算机网络协议大体分为5层:应用层、传输层、网络层、数据链接层和物理层。其中,上层仅仅只需在下层所提供的服务之上构建自己的服务,而不用关心它的下下层所提供的服务。例如,http应用要完成相应的功能就只需考虑传输层tcp所能提供的服务,不需要知道网络层能提供什么服务。

​ 回到内存分配的问题上来,其实,应用程序的内存分配也有明确的层级概念。当应用程序需要内存时,它管c++标准库要。所以应用程序只需知道c++标准库能给它提供内存配置的功能就够了。c++的new算子(操作符)1 提供虚拟内存分配的能力,这一点,c++程序员们心知肚明 2。通过编译器解释之后,new算子被展开,然后调用c++标准库提供的全局operator new(std::sizt_t)函数申请内存。

当你写下:

Foo* p = new Foo();

经编译器解释,然后生成类似下面的代码:

Foo* p;

 // don't catch exceptions thrown by the allocator itself
 void* raw = operator new(sizeof(Foo)); //step 1:调用全局的operator new操作符申请虚拟内存。

 // catch any exceptions thrown by the ctor
 try {
   //step 2:调用placement new函数在raw开始的虚拟地址位置构造一个Foo对象。
   //等价于((Foo*)raw)->Foo::Foo();
   p = new(raw) Foo();  // call the ctor with raw as this
 }
 catch (...) {
   // oops, ctor threw an exception
   operator delete(raw);//step 3:如果构造函数抛出异常,则将分配的内存返还。
   throw;  // rethrow the ctor's exception
 }

值得注意的是,当构造函数出现异常时,并不会调用析构函数,不过,会调用对象内部的非指针成员变量的析构函数3。所以如果直接用原始指针作为成员变量,那么在对象构造函数为指针成员变量分配资源之后,成功返回之前,如果抛出了异常,那就会导致资源泄漏4。所以,强调一下Koening&Moo认为的c++最重要的三个建议之一: 避免使用指针。但是完全不用指针几乎是不可能的,所以,作为替代,可以而且必须选择智能指针。

​ 仔细考察后发现new的关注点是替应用程序向堆索要内存,并向上层应用提供虚拟内存。如果不考虑细节,new的行为其实等同于malloc的行为5。既然new提供向堆索要内存的服务,那么,它可以把精力集中在怎么与堆交互上--如何扩展有效堆内存,如何维护有效堆内存。

如何扩展有效堆内存

​ 每个进程都有标准的虚拟空间布局:代码段、数据段、向上增长的堆以及向下的栈还有最顶层的受保护的系统空间。这里,有效堆空间是可以动态增长的,栈区的大小一般初始化为1M,所以,就地址可访问性来说,如果不特别设置,栈区的大小是固定的。过小的栈空间容易导致段错误就是这个原因。你可以试试定义一个大数组,或者搞个深度非常大的递归调用来观察这个现象6

进程某时刻的快照:


p146 进程虚拟地址空间

​ 快照中位于有效堆顶与栈之间的虚拟地址区间(图中虚拟地址区间(0x080DE000, 0XBF7EC000])对进程来说是无效的,这意味着这部分的内存不可访问,也就是说,这时如果要执行一条访存的cpu指令,且被访问的内存地址落在(0x080DE000, 0XBF7EC000]区间的话,会引发一个段错误,应用程序就死掉了。这里发生了什么呢?

​ 解释上面的现象需要一定的操作系统基础。当cpu拿到一条访存指令时,它会到指令所给出的虚拟地址处去取数据,我们知道,硬件部分有一个叫MMU的单元,它负责将一个虚拟地址翻译成对应的物理地址。在这个翻译过程中,MMU会检查地址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值