1、内存泄漏基础:什么是内存泄漏、内存泄漏的危害、常见的内存泄漏类型
1.1 什么是内存泄漏?
内存泄漏,说白了就是「借了不还」。
你向操作系统申请了一块内存,用完之后忘了释放。这块内存就永远「失踪」了——别人用不了,系统也收不回来。
我刚开始做C++开发时,觉得这玩意儿离自己很远。直到有一次,我写了一个图像处理模块,跑了一整天,内存从200MB涨到了2GB。嗯,那时候我才真正意识到:内存泄漏不是小问题。
用代码说话:
// 典型的内存泄漏
void leakExample() {
int* p = new int(42); // 申请内存
// 忘记 delete p;
// p 指向的内存再也无法释放
}
你想想看,每次调用这个函数,就丢失4个字节。如果被循环调用一万次呢?十万次呢?
核心定义:内存泄漏是指程序在运行过程中,动态分配的内存没有被正确释放,导致这部分内存无法被再次使用,直到程序结束。
1.2 内存泄漏的危害
很多人觉得:「程序退出时系统会回收所有内存,怕什么?」
这话没错,但问题在于——你的程序可能根本不会正常退出。
我遇到过最惨的一次:一个Qt桌面应用,用户连续操作8小时后,界面卡死,鼠标都动不了。最后发现是某个定时器回调里,每次new一个QWidget,却从不delete。
内存泄漏的危害,我总结为四点:
| 危害 | 表现 | 后果 |
|---|---|---|
| 性能下降 | 程序越来越慢 | 用户抱怨,体验极差 |
| 内存耗尽 | OOM(Out of Memory) | 程序崩溃,数据丢失 |
| 系统不稳定 | 拖慢整个系统 | 其他进程也被影响 |
| 难以排查 | 问题复现困难 | 开发成本飙升 |
警告:在嵌入式设备或服务器端,内存泄漏是致命的。我曾经见过一个后台服务,运行两周后内存占用达到90%,最终被系统kill掉。那是一个线上事故,凌晨3点被电话叫醒的感觉……你懂的。
1.3 常见的内存泄漏类型
搞清楚了危害,咱们来看看实际开发中,内存泄漏到底长什么样。我把它分成三类:
类型一:堆内存泄漏
这是最常见的。new了没delete,malloc了没free。
// 堆内存泄漏示例
void processData() {
char* buffer = new char[1024];
// 处理数据...
// 忘记 delete[] buffer;
}
我个人习惯:能用智能指针就用智能指针。std::unique_ptr、std::shared_ptr,它们能帮你自动管理生命周期。
类型二:资源泄漏
不仅仅是内存。文件句柄、数据库连接、GDI对象、Qt的QPixmap……这些都属于系统资源。
// 资源泄漏示例(Qt中常见)
void loadImages() {
QPixmap* pix = new QPixmap("big_image.png");
// 用完后忘记 delete pix;
// 每次调用都泄漏一个QPixmap对象
}
我曾经在项目中排查过一个诡异问题:程序运行一段时间后,所有图片都显示不出来。最后发现是QPixmap对象泄漏,导致GDI句柄耗尽。Windows下每个进程的GDI句柄上限是10000个,超过了就罢工。
类型三:逻辑泄漏
这种最隐蔽。内存释放了,但释放的时机不对,或者释放的方式不对。
// 逻辑泄漏示例
class MyWidget : public QWidget {
QTimer* m_timer;
public:
MyWidget() {
m_timer = new QTimer(this); // 父对象管理
// 看起来没问题,但...
}
};
// 如果手动 delete MyWidget 对象,但忘记 stop 定时器
// 定时器回调中可能还在访问已销毁的对象
// 这叫野指针,比内存泄漏更可怕
避坑指南:我曾经在Qt中犯过一个低级错误——在析构函数里没有调用deleteLater(),导致QObject的子对象没有被正确清理。嗯,Qt的父子对象机制虽然方便,但千万别以为它万能。
1.4 知识体系总览
为了让你更直观地理解本章内容,我画了一张图:
这张图把本章的核心内容串起来了。从定义到危害,再到三种常见类型,一目了然。
最后说一句:内存泄漏不是洪水猛兽。你只要养成好习惯——用智能指针、注意资源释放、定期做内存分析——它基本不会找上你。
嗯,下一章咱们聊聊怎么用工具定位内存泄漏。不过那是后话了。
本章小结:
- 内存泄漏 = 申请了内存但没释放
- 危害包括性能下降、内存耗尽、系统不稳定、排查困难
- 三种常见类型:堆内存泄漏、资源泄漏、逻辑泄漏
- 预防比修复更重要
可可精品资料库