一、C++ 内存区域整体划分(经典五区)
┌───────────────┐
│ 栈 (Stack) │ ← 函数调用、局部变量
├───────────────┤
│ 堆 (Heap) │ ← new / malloc
├───────────────┤
│ 全局/静态区 │ ← global / static
├───────────────┤
│ 常量区 │ ← 字符串字面量、const
├───────────────┤
│ 代码区 │ ← 程序指令
└───────────────┘
1. 高频坑点
2. 栈(stack)
高地址
|
| [栈底] ← 初始位置(固定)
| ↓
| [数据]
| ↓
| [栈顶] ← 动态移动(随压栈/出栈变化)
|
低地址
3. 堆(heap)
高地址
|
| [栈底] ← 栈从高地址开始,向低地址扩展
| ↓
| [栈顶]
|
| (空闲内存区域)
|
| [堆末尾] ← 堆从低地址开始,向高地址扩展
| ↑
| [堆起始地址]
低地址
4. 静态区(static/global 区)
特点: 1.程序启动时分配 2.程序结束时释放 3.整个程序生命周期
存什么:
int g = 10; // 全局变量
static int s = 20; // 静态变量
void foo() {
static int x = 0; // 局部静态变量
}
| 分配方式 | 自动 | 手动 | 自动 |
| 释放方式 | 自动 | 手动 | 程序结束 |
| 生命周期 | 函数级 | 程序员控制 | 程序级 |
| 大小 | 小 | 大 | 中 |
| 速度 | 高 | 低 | 中 |
| 泄漏风险 | 无 | 高 | 低 |
二、内存泄漏的本质与核心原因(面试必讲)
1. 内存泄漏的标准定义
堆内存泄漏:程序员通过new/malloc在堆上分配了内存,后续既没有执行delete/free释放,又失去了所有指向该堆内存的指针,导致这块内存成为「不可访问的垃圾内存」—— 程序运行期间无法再使用,也无法释放,直到程序退出后由操作系统回收。
//new 了不 delete
void foo() {
int* p = new int(10);
} // 忘记 delete
//指针被覆盖
int* p = new int(10);
p = new int(20); // 原内存丢失
//️ new[] / delete 不匹配
int* arr = new int[10];
delete arr; // 应该 delete[]
️//循环容器中持续 new
for (;;) {
new int(1);
}
//️ 异常提前 return
int* p = new int;
if (error) return; // 泄漏
️// 智能指针循环引用
shared_ptr<A> a;
shared_ptr<B> b;
a->b = b;
b->a = a; // 永不释放
2. 核心根源:堆内存的「手动管理特性」
栈和静态区无泄漏的原因是无需程序员干预,而堆内存是程序员全权负责的: 编译器 / 操作系统不会主动回收堆内存(即使指针失效); 只要程序员未执行释放操作,且指向堆内存的指针被销毁 / 置空 / 重赋值,就会导致泄漏。
三、内存泄漏的排查与根治方法
1.代码规范自查
2.源头根治泄漏
1.核心思想:RAII 原则(资源获取即初始化) 定义:将堆资源的分配(new) 绑定到对象的构造函数,将资源的释放(delete) 绑定到对象的析构函数—— 对象创建时分配资源,对象销毁时(栈帧销毁 / 作用域结束)自动调用析构释放资源,无需程序员手动管理。 本质:用栈的自动管理特性,管理堆的手动管理资源,从根源避免泄漏。
核心实现:C++11 智能指针(RAII 的具体实现) 智能指针是封装了堆内存指针的模板类,本质是栈上的对象,析构时自动释放指向的堆内存,支持*和->操作,用法和普通指针一致,完全替代手动new/delete。



