你真的理解内存分配吗?
时间:2021-09-29 15:17:44
手机看文章
扫描二维码
随时随地手机看文章
[导读]内存是计算机中必不可少的资源,因为CPU只能直接读取内存中的数据,所以当CPU需要读取外部设备(如硬盘)的数据时,必须先把数据加载到内存中。我们来看看可爱的内存长什么样子的吧,如图1所示:一、内存申请通常使用高级语言(如Go、Java或Python等)都不需要自己管理内存(因为有垃圾回收机制),但C/C程序员就经常要与内存打交道。当我们使用C/C编写程序时,如果需要使用内存,就必须先调用malloc函数来申请一块内存。但是,malloc真的是申请了内存吗?我们通过下面例子来观察malloc到底是不是真的申请了内存:1#include 23int main(int argc, char cons...
内存是计算机中必不可少的资源,因为 CPU 只能直接读取内存中的数据,所以当 CPU 需要读取外部设备(如硬盘)的数据时,必须先把数据加载到内存中。我们来看看可爱的内存长什么样子的吧,如图1所示:
一、内存申请
通常使用高级语言(如Go、Java 或 Python 等)都不需要自己管理内存(因为有垃圾回收机制),但 C/C 程序员就经常要与内存打交道。当我们使用 C/C 编写程序时,如果需要使用内存,就必须先调用 malloc
函数来申请一块内存。但是,malloc
真的是申请了内存吗?我们通过下面例子来观察 malloc
到底是不是真的申请了内存: 1#include
2
3int main(int argc, char const *argv[])
4{
5 void *ptr;
6
7 ptr = malloc(1024 * 1024 * 1024); // 申请 1GB 内存
8
9 sleep(3600); // 睡眠3600秒, 方便调试
10
11 return 0;
12}
上面的程序主要通过调用 malloc
函数来申请了 1GB 的内存,然后睡眠 3600 秒,方便我们查看其内存使用情况。现在,我们编译上面的程序并且运行,如下:1$ gcc malloc.c -o malloc
2$ ./malloc
并且我们打开一个新的终端,然后查看其内存使用情况,如图 2 所示:图2 中的
VmRSS
表示进程使用的物理内存大小,但我们明明申请了 1GB 的内存,为什么只显示使用 404KB 的内存呢?这里就涉及到 虚拟内存
和 物理内存
的概念了。二、物理内存与虚拟内存
下面先来介绍一下物理内存
与 虚拟内存
的概念:物理内存
:也就是安装在计算机中的内存条,比如安装了 2GB 大小的内存条,那么物理内存地址的范围就是 0 ~ 2GB。虚拟内存
:虚拟的内存地址。由于 CPU 只能使用物理内存地址,所以需要将虚拟内存地址转换为物理内存地址才能被 CPU 使用,这个转换过程由MMU(Memory Management Unit,内存管理单元)
来完成。虚拟内存
大小不受物理内存
大小的限制,在 32 位的操作系统中,每个进程的虚拟内存空间大小为 0 ~ 4GB。
malloc
函数申请的内存都是虚拟内存。实际上,内核会为每个进程管理其虚拟内存空间,并且会把虚拟内存空间划分为多个区域,如 图3 所示:我们来分析一下这些区域的作用:
代码段
:用于存放程序的可执行代码。数据段
:用于存放程序的全局变量和静态变量。堆空间
:用于存放由malloc
申请的内存。栈空间
:用于存放函数的参数和局部变量。内核空间
:存放 Linux 内核代码和数据。
三、brk指针
由此可知,通过malloc
函数申请的内存地址是由 堆空间
分配的(其实还有可能从 mmap
区分配,这种情况暂时忽略)。在内核中,使用一个名为 brk
的指针来表示进程的 堆空间
的顶部,如 图4 所示:所以,通过移动
brk
指针就可以达到申请(向上移动)和释放(向下移动)堆空间的内存。例如申请 1024 字节时,只需要把 brk
向上移动 1024 字节即可,如 图5 所示:事实上,
malloc
函数就是通过移动 brk
指针来实现申请和释放内存的,Linux 提供了一个名为 brk()
的系统调用来移动 brk
指针。四、内存映射
现在我们知道,malloc
函数只是移动 brk
指针,但并没有申请物理内存。前面我们介绍虚拟内存和物理内存的时候介绍过,虚拟内存地址必须映射到物理内存地址才能被使用。如 图6 所示:如果对没有进行映射的虚拟内存地址进行读写操作,那么将会发生
缺页异常
。Linux 内核会对 缺页异常
进行修复,修复过程如下:- 获取触发
缺页异常
的虚拟内存地址(读写哪个虚拟内存地址导致的)。 - 查看此虚拟内存地址是否被申请(是否在
brk
指针内),如果不在brk
指针内,将会导致 Segmention Fault 错误(也就是常见的coredump),进程将会异常退出。 - 如果虚拟内存地址在
brk
指针内,那么将此虚拟内存地址映射到物理内存地址上,完成缺页异常
修复过程,并且返回到触发异常的地方进行运行。
五、总结
本文主要解释了内存申请的原理,并且了解到malloc
申请的只是虚拟内存,而且物理内存的申请延迟到对虚拟内存进行读写的时候,这样做可以减轻进程对物理内存使用的压力。- EOF -