C与C++的内存布局

$fix: .data\ first,\ then\ .bss$
1 编译时间确定
这些段的大小与位置在运行之前就会被编译器与链接器确定。
1.1 代码(文本)段
.text文本段存放程序经过编译 - 汇编 - 链接后生成的机器码。
1 | graph LR |
1.2 静态数据段
1.2.1 只读数据段
该段落存放常量,包括字符串常量,const 修饰的全局变量以及硬编码数据等。
1.2.2 数据段
该段落存放具有初值的全局或者局部变量。
1.2.3 BSS段
.bss 段也就是 Blocked Start By Symbol ,存放未初始化或者初始化为 0 的变量。
2 运行时间确定
这些内存空间在程序运行时动态进行分配。
2.1 堆
堆是一个用于动态分配的内存区。这片区域的内存由程序员手动申请与管理。
| C | C++ | |
|---|---|---|
| malloc | new | |
| free | delete | 忘记释放内存将会导致内存泄漏 |
在操作系统中,堆在逻辑上是无限的,其大小取决于整台电脑的虚拟内存。
堆可能出现几个问题:
- 内存泄漏:程序申请了一块内存,使用之后却没有进行归还,导致这块内存空间永久被占用。
- 内存碎片:当频繁地申请和释放内存时,内存空间会被分割为很多大小不一的块,那么内存空间就会碎片化,导致不能一次性申请一个连续的大内存块,即使此时碎片空间的总和大于这个需求,由于不连续,这些空间不能这样利用。
堆的速度比较慢,这是因为堆空间总是需要通过指针来访问,而且申请释放堆空间函数的逻辑也比较复杂。
由于内存碎片的产生,堆上的数据往往散落在 RAM 的各个角落(分散内存)。当 CPU 尝试读取一组数据时,如果它们不连续,就会频繁触发 Cache Miss,被迫去慢速的物理 RAM 中读取数据。
2.2 栈
栈是一块 总大小在编译时确定 (但其内存空间是动态调用的),由编译器而非程序员维护的内存区域。其主要用于局部变量、函数形参以及返回地址的存储以及函数的调用。
栈与队列是相反的,它遵循的是先进后出,后进先出的一种数据结构。
当我们调用一个函数的时候,首先将它的函数调用栈,也就是栈帧压入栈中。一个栈帧包含了这个函数的一些信息,比如说
- 调用(Caller)的上下文
- 局部变量
- 函数的参数与返回地址
当函数执行完返回的时候,这个栈帧被弹出,函数所占用的内存空间也立刻会被回收。
栈的读取写入速度相比于堆非常快,这是因为栈不需要程序员来操作,而是由 CPU 指令集支持,并且栈的空间结构规整,不会出现堆的碎片化以及 Cache Miss 问题。
栈中可能会出现这些问题:
- 栈溢出,StackOverflow : 当程序使用的空间超过了整个栈的大小时,栈溢出就会发生。这可能由于函数的深度递归或者是一个巨大的局部变量所造成。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Clairvoyance!
