深入了解内存泄漏:定义、与溢出区别及常见场景

检测维修 0 157

程序中存在对象持续占据内存的情况,这些对象虽然已经失去作用,却依然被某些引用所牵绊,使得垃圾回收机制无法将其清除,最终造成内存资源不断被消耗。

这好比器皿有了破损,液体持续流失,最后容器内的物质会逐渐枯竭。

内存泄漏 ≠ 内存溢出

内存泄漏和内存溢出是两个不同的概念,前者是指内存未被释放却持续占用,后者则是指程序申请内存时系统无足够空间供给,进而引发程序崩溃或异常状况。这好比往一个容量固定的桶里注水,一旦水量超出桶的容量就会发生溢出现象。

所以说,长期的内存泄漏最终会导致内存溢出。

内存泄漏常见场景

内存泄漏的常见场景如下:

使用 ThreadLocal 过程中存在错误:线程工作结束后没有对 ThreadLocal 进行清除,具体实现代码展示如下:

// ThreadLocal未 remove 导致泄漏
private static ThreadLocal threadLocal = new ThreadLocal<>();
启动请求处理函数,首先接收输入数据,然后进行验证,接着执行核心逻辑,随后生成响应结果,最后返回数据给客户端。
局部变量存储了一个大型对象的实例,并将其关联到当前线程上下文。
使用完成后没有执行 threadLocal 的移除操作
}

未关闭资源:文件流、数据库连接等未关闭,示例代码如下:

// 文件流未关闭导致泄漏
开放方法文件泄露,或抛出异常
创建一个文件输入流对象,该对象用于读取名为largeFile.txt的文件内容,通过构造方法指定文件路径
// 忘记调用 fis.close()
}

监听器未注销:注册的事件监听器未移除,示例代码如下:

public class EventSource {
    private List listeners = new ArrayList<>();

内存泄漏场景_内存溢出检测工具_内存泄漏定义

公共方法添加监听器对象,参数为事件监听接口实现类实例,通过该接口实现类对象接收事件通知,增强类的交互能力,提高模块的扩展性和可维护性,确保系统能够及时响应外部事件的变化,并做出相应的处理动作。 添加了监听者,但是没有提供移除监听者的功能 } }

静态集合滥用:静态集合长期持有对象引用,示例代码如下:

// 静态集合导致内存泄漏
private static List cache = new ArrayList<>();
公开方法将对象存入缓存,参数为对象实例,完成对象添加操作,确保缓存更新及时,调用后缓存内容发生变化
cache.add(obj); // 添加后从不移除
}

错误的 equals 或 hashCode 方法:造成 HashMap 无法准确判断对象是否相等,具体实现如下:

未重写 equals 方法或 hashCode 方法会造成 HashMap 出现内存泄漏现象
Map map = new HashMap<>();
for(int i=0; i<100; i++) {
    map.put(new Person("jon"), 1); // 每次都被视为新对象
}

内存泄漏排查

内存泄漏的排查步骤如下:

监控内存使用趋势:堆转储分析:代码审查:小结

内存中存在无用对象,这些对象持续占据空间,因为它们还保留着被引用的状态,垃圾清理机制无法将其清除,这就是内存泄漏现象。这种现象会不断消耗内存资源,时间长了,可能会引发内存不足的情况。但内存泄漏和内存溢出是两个不同的概念,这一点在面试时需要特别留意。

本文已收录到我的面试小站

www.javacn.site

这个网址提供多种技术资料,涵盖了场景案例、人工智能领域中的SpringAI和SpringAIAlibaba、多线程处理方法、数据库MySQL和Redis、Spring系列框架中的Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、Java虚拟机JVM、设计模式理论、消息传递系统、Dify平台、Coze工具、人工智能相关的常见面试问题等主题内容。

相关推荐: