1. 内存泄露的本质与危害
内存泄露就像家里忘记关水龙头,水不断流失却无人察觉。作为开发者最头疼的问题之一,它指的是程序在运行过程中未能释放不再使用的内存空间。这些"僵尸内存"会不断累积,最终导致系统性能下降甚至崩溃。
我处理过最典型的一个案例是某电商APP在用户频繁浏览商品页面时,内存占用每小时增长200MB。通过MAT工具分析发现,是商品图片加载器持有Activity引用导致的上下文泄露。这种问题在Android开发中尤为常见,往往要等到OOM崩溃时才会暴露。
内存泄露与内存溢出(OOM)是兄弟概念但本质不同:
- 泄露是内存无法回收(该释放的没释放)
- 溢出是内存不足(需要的内存超过限额)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内存泄露的四大元凶
2.1 静态集合类引用
java复制// 典型错误示例
public class DataHolder {
public static List<Object> cache = new ArrayList<>();
}
// 使用后未清理
DataHolder.cache.add(bigObject);
静态集合的生命周期与类加载器相同,如果不手动清除,其中的对象会永久驻留内存。我曾见过一个Spring Boot应用因为将DTO存入静态Map,运行一周后吃掉16GB内存。
解决方案:
- 使用WeakHashMap替代普通Map
- 添加清理入口方法
- 限制缓存大小并实现淘汰策略
2.2 未关闭的资源
数据库连接、文件流、Socket等资源忘记关闭,不仅占用内存还可能耗尽系统资源:
java复制// 错误示范
try {
FileInputStream fis = new FileInputStream("large.txt");
// 使用后未关闭
} catch (IOException e) {
e.printStackTrace();
}
最佳实践:
java复制// Java 7+ try-with-resources
try (FileInputStream fis = new FileInputStream("large.txt")) {
// 自动关闭
}
2.3 监听器未注销
在事件监听模型中,注册后忘记注销会导致发布者持有观察者的强引用:
java复制// Android典型内存泄露
sensorManager.registerListener(this);
// onDestroy时未unregister
处理方案:
- 使用WeakReference包装监听器
- 在生命周期结束时对称注销
- 采用RxJava等自动管理订阅的框架
2.4 ThreadLocal使用不当
ThreadLocal的经典泄露场景:
java复制public class UserContext {
private static ThreadLocal<User> currentUser = new Thread
