JVM 内存泄漏
内存分配
在 JVM 中,内存泄漏(Memory Leak)的核心定义是:存在一些对象,它们已经不再被程序使用(没有任何业务意义),但由于仍然被 GC Roots(根对象)直接或间接引用,导致 GC(垃圾回收器)无法回收它们,最终占用的内存越来越多,直至引发 OOM(内存溢出)。
内存泄漏是 “缓慢积累” 的过程,而 OOM 是泄漏的最终结果之一
堆内存泄漏
堆内存用于存储对象实例,当对象 “无用但可达” 时,就会发生泄漏
- 静态集合(如 static List/Map)的生命周期与 JVM 一致。如果向静态集合中添加对象后,未在使用完后移除,这些对象会一直被集合引用,导致无法回收。
- 文件流(FileInputStream)、数据库连接(Connection)、网络连接(Socket)等资源对象,不仅占用堆内存,还关联底层系统资源。如果未调用 close()释放,这些对象会被 JVM 持有引用(如通过 finalize 机制等待回收),但回收周期长,且可能因资源句柄耗尽导致泄漏。
- 非静态内部类(成员内部类)会隐式持有外部类的实例引用。如果内部类对象被长期引用(如放入静态集合),会导致外部类实例也无法被回收,即使外部类已经无用
- 使用 HashMap 等容器做缓存时,如果只添加缓存不清理过期数据,缓存会无限增长。即使缓存的 key/value 已无用,只要被缓存引用,就不会被 GC 回收。
- ThreadLocal 用于线程内共享变量,其内部通过 Thread 的 threadLocals(一个 ThreadLocalMap)存储数据。如果 Thread 长期存活(如线程池中的核心线程),且 ThreadLocal 对象被回收,但 ThreadLocalMap 中的 Entry 未被清理(key 为 null,但 value 仍存在),会导致 value 对象泄漏。
解决
及时释放资源,慎用静态集合,合理管理缓存
内存溢出
内存溢出是指 JVM 在尝试分配内存时,发现目标内存区域已无足够空间,且垃圾回收(GC)后仍无法释放出可用空间,最终抛出 java.lang.OutOfMemoryError 异常
溢出场景
- 堆内存溢出:
- 短时间内创建大量对象,堆内存不足以容纳
- 内存泄漏长期积累,堆被占满
- 堆配置过小
- 方法区溢出: + 频繁动态生成类 + 常量池过大
等等,除程序计数器外,所有区域都可能发生溢出
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Myskill-blog!