背景
内存泄漏一般指的是堆内存泄漏。在写代码的时候,通过C++运算符或者库函数(malloc等)分配内存,使用完后总是忘记释放内存,导致操作系统失去对这块内存的控制,也就是内存泄漏。
如果不进行内存泄漏的处理,当系统中运行的程序越来越多,内存泄漏也就会越来越多,即使实际上物理内存足够大,但是一旦内存泄漏越来越多,可用内存就越来越少,严重可能会导致系统崩溃。
如何定位可能发生内存泄漏的进程
模拟一个进程,该进程在运行时一直发生内存泄漏,代码如下
1 | #include<iostream> |
该代码一直在申请10000B字节,因为每次申请时都会覆盖掉申请内存的起始地址,所以该进程在运行时会一直发生内存泄漏。
发生内存泄漏的进程有一个特点,就是改进程在运行的过程中,其物理空间和虚拟空间会一直增加,可以通过Linux的 “ps aux| grep 监测进程名” 获得监测进程的pid,然后通过 watch 命令动态查看监测进程其内存的使用情况
1 | ps aux|grep 进程名 |


可以看出,随着时间的增长,进程13875的物理内存,虚拟内存,内存使用率一直增加,内存泄漏的进程一定是满足这个特征,但是满足这个特征的进程不一定是内存泄漏,可以使用watch命令观察一段时间。然后通过内存泄漏工具做具体的内存泄漏排查,常用的内存泄漏工具是Valgrind。
Valgrind工具安装
step1:下载安装包
1 | wget https://sourceware.org/pub/valgrind/valgrind-3.16.1.tar.bz2 |
step2:解压安装包
1 | tar -jxvf valgrind-3.16.1.tar.bz2 |
step3:配置Valgrind,生成Makefile文件
1 | ./configure |
step4:编译 Valgrind
1 | make |
step5:安装 Valgrind
1 | sudo make install |
内存泄漏代码样例
1 | #include<iostream> |

这段代码可以看到 int* p = new int[5] ,指针变量p 和new 创建的一块内存的存储区域,指针 p存储在栈中, new 创建的空间存储在堆空间中,当这个函数执行完后,栈空间的内存会被回收,指针p会被自动销毁,但是new 创建的20个字节不会自动释放,这就造成了内存泄漏。
Valgrind 检查内存泄漏
查看全局进程内存泄漏大小
可以看出,泄漏了20个字节,和前面分析一致
1 | g++ -g -o test oom.cpp |

定位具体行泄漏原因
1 | g++ -g -o test oom.cpp |

我们可以看到,导致内存泄漏的是在代码的第5行,因为 new int[5]导致的。
如何解决内存泄漏
可以手动释放分配的内存
new 和 delete 搭配, new[] 和 delete[] 搭配。
但是这种方法不建议在日常开发中使用,因为可能会因为疏漏造成内存泄漏。
1
2
3
4
5
6
7
8#include<iostream>
using namespace std;
int main() {
int* p = new int[5];
delete[] p;
return 0;
}
使用智能指针
智能指针实际上就是普通指针的封装,在离开作用域时会对申请的资源进行销毁
1
2
3
4
5
6
7
8#include <iostream>
#include <memory>
using namespace std;
int main() {
shared_ptr<int[]> p(new int[5]); // 自动使用 delete[]
return 0; // 自动释放,无内存泄漏
}使用RALL技术
实际上智能指针 shared_ptr也属于RALL技术,资源的生命周期和对象的生命周期一致,在更复杂的开发过程中,可以将资源的申请和释放分装成一个类,在这个类当中写好构造和析构,当类创建对象时,资源也被创建,当对象被销毁时,资源也被释放,从而避免了内存泄漏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <iostream>
using namespace std;
class IntArray {
private:
int* data; // 存储动态数组
size_t size; // 数组大小
public:
// 构造函数:分配内存
IntArray(size_t n) : size(n), data(new int[n]) {
cout << "Allocated " << n << " integers." << endl;
}
// 析构函数:释放内存
~IntArray() {
delete[] data;
cout << "Freed memory." << endl;
}
// 访问元素(可添加边界检查)
int& operator[](size_t index) {
return data[index];
}
};
int main() {
IntArray arr(5); // 自动分配 5 个 int
arr[0] = 10; // 像普通数组一样访问
// 不需要手动 delete,析构时自动释放
return 0;
}