冰山之下:使用new申请内存的背后

本文探讨了C++动态内存申请背后的原理,从进程地址空间、内存分配策略到标准库的实现,特别是libstdc++和glibc的malloc。在分析内存分配失败时,提出了需要检查客户编译链、系统配置以及优化内存消耗的思路。
摘要由CSDN通过智能技术生成

起因

客户在使用动态链接库的过程中发生异常,种种原因导致目前无法获取到该库的源码,客户方面也没有给出足够的信息,包括交叉编译工具链、系统配置等。由于是生产库,输出信息也少的可怜,但是不管怎么样还是要恰饭的,要恰饭就要解决问题,也就是说只能硬着头皮上,至少先缩小问题的范围。唯二的信息就是库抛出的std::bad_alloc异常,这是C++中动态申请内存失败后抛出的,以及内核报出的内存不足(OOM)。
虽然好像板上钉钉,罪魁祸首就是内存不足,但是从提出申请到内核批复内存不足中间程序还很多,我们需要有充分的证据才能信誓旦旦的说这就是内存不足引起的,你加内存条吧。毕竟,如果一开始方向错了,再努力也解决不了问题。
想要解决问题,就得先知道异常从哪来;想知道异常从哪来就要知道动态申请内存的原理。虽然千头万绪,但还不至于无从下口。总得来说,需要解决以下几个问题:

  1. 动态内存从哪里来;
  2. 动态内存怎么申请;

##动态内存从哪里来

进程地址空间

一般编程语言都会介绍内存分配有两种方式:动态分配和静态分配。静态分配就是编译的时候已经确定了内存的大小和位置;动态分配就是程序在运行过程中动态申请内存,内存的大小和位置都是编译的时候不能确定的。静态的容易理解,需要多少内存创建进程的时候就知道了,但是动态分配,存在着不确定性,怎么去处理?对于这个问题,不同系统有不同的内存分配策略,由于这里涉及到的问题出现在Linux上,因此以下主要围绕Linux下的内存分配策略展开。

Linux是个多任务操作系统,同一时段内有多个进程在运行着,它们各自拥有很多的资源,其中比较重要的资源之一就是它们拥有的内存。但毕竟内存资源是有限的,不管你4G、8G或者是64G内存,总归是有个尽头。一个进程可能就耗费几百上千兆内存,操作系统怎么让有限的内存运行更多的进程?解决办法是虚拟内存技术。

操作系统将管理的内存抽象成了一个虚拟内存,这个虚拟的内存用一个线性地址来表示。例如,32位的系统的拥有一个从0到 232 这么大的一个线性地址,也就是4GB,而64位的系统拥有的线性地址非常大:0~264,从目前来看这个虚拟内存的容量是可以满足任何进程的需求的。这个线性地址被分成了许多段,每一段大小相当,一般是4KB,每一段称为一页。而真的物理内存也被分为相同大小的一块一块区域,称为帧。这样,虚拟内存的一页就能和物理内存的一帧相映射,同一个物理内存的帧可以与多个虚拟内存的页做映射,而物理内存的每一帧又可以和辅存如硬盘上的文件相映射,如下图1所示。操作系统通过换页机制(paging),这就相当于可拥有了一块远大于物理内存的虚拟内存。

举个栗子,如图1所示,有两个虚拟内存的页映射到了同一个物理内存帧,但同一时间内只能有一个页和帧绑定。某一时刻,frame存储的是page1的内容,现在程序访问到了page2,由于page2的目前还没和frame绑定,就会触发缺页中断(page fault)。操作系统先挂起当前程序,然后中断处理程序就会将目前物理内存帧内的内容存储到磁盘block1,而把block2的内容读到frame,然后将page2绑定到frame,最后恢复被挂起的程序的执行。通过这一换页机制,程序就拿到了page2上的存储的内容。当又需要page1的内容的时候,同样的方法得到,而应用程序并不知道这些,在应用程序看来,它需要的数据一值都在内存中。

1585322797803.jpeg

有了这么大的内存,同时运行多个进程就变成了可能。操作系统为每个进程分配了一个互不重叠的线性地址空间(Linear Address Space),这个线性地址空间可以有多个内存区域(Region)组成,每个进程的地址空间是相互独立的。例如下图2所示,分别用两种颜色表示出了两个进程相互独立的地址空间。
Picture 2 Process Address Space
Linux使用一个mm_struct的结构体来存储进程地址空间的信息,对于地址空间中的每一个区域,使用vm_area_struct结构体来存储,这些一个个区域按照地址递增的顺序使用单向链表链接在一起,如图2所示。并且为它们构建了一棵红黑树,使得在数量庞大内存区域中检索特定的一个区域的时候也能获得很好的检索性能1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值