Java导致内存泄漏的多种情况分析
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>1、生命周期长的集合</li><li>2、未关闭的资源</li><li>3、ThreadLocal 使用不当</li><li>4、内部类与外部类引用</li><li>5、 监听器与回调</li></ul></div><p>今天我们来一起聊一聊有哪些情况会导致内存泄漏。</p><p>什么是 <strong>内存泄漏</strong> 呢?</p>
<p><strong>内存泄漏</strong> 是指对象 <strong>已经不再被程序使用</strong>,但因为某些原因 <strong>无法被垃圾回收器回收</strong>,长期占用内存,最终可能引发 <strong>OOM</strong>(OutOfMemoryError)。</p>
<p>接下来我们看一下常见的几类内存泄漏场景。</p>
<p class="maodian"></p><h2>1、生命周期长的集合</h2>
<p>将对象放入 <strong>静态</strong> 或 <strong>生命周期很长 </strong>的集合(如 public <strong>static</strong> List<Object> list = new ArrayList<>();),即使后面不再需要,集合仍持有其引用,导致无法<strong>GC</strong>。</p>
<p class="maodian"></p><h2>2、未关闭的资源</h2>
<p>连接、流等资源未调用<strong> close() </strong>方法关闭。这些资源不仅占用内存,还可能占用文件句柄(操作系统分配的唯一标识,凭它,你才能操作文件资源)、网络连接等系统资源。比如 数据库连接、文件流(FileInputStream)、Socket连接 等。</p>
<div class="jb51code"><pre class="brush:java;">public class FileTest {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("test.txt");
// 读取文件,未调用 fis.close()
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
// 未调用 fis.close() → fis 持有 Native 引用,无法回收
}
}
}</pre></div>
<p class="maodian"></p><h2>3、ThreadLocal 使用不当</h2>
<p>将对象存入 <strong>ThreadLocal </strong>后,未在后续调用 <strong>remove()</strong> 清理。若线程来自线程池(会复用),其 ThreadLocalMap 中的值会一直存活。</p>
<div class="jb51code"><pre class="brush:java;">public class ThreadLocalTest {
private static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
public static void main(String[] args) {
// 线程池(核心线程长期存活)
TThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new ThreadFactoryBuilder().setNameFormat("my-thread-pool-%d").setDaemon(false).setPriority(Thread.NORM_PRIORITY).build(),
new ThreadPoolExecutor.AbortPolicy()
);
executor.submit(() -> {
User user = new User("李四", 30);
userThreadLocal.set(user); // 存储到 ThreadLocal
// 业务执行完毕,未调用 remove()
// 核心线程不会销毁,ThreadLocal 仍持有 user 引用
});
}
}</pre></div>
<p>ps:未进行 <strong>remove()</strong>,还可能会导致 <strong>ThreadLocal </strong>取值串门。</p>
<p class="maodian"></p><h2>4、内部类与外部类引用</h2>
<p>非静态内部类(或匿名类)会 <strong>隐式持有 </strong>外部类的引用。如果内部类实例生命周期更长(如被缓存或另一个线程引用),会阻止外部类被回收。</p>
<div class="jb51code"><pre class="brush:java;">public class OuterClass {
private byte[] bigData = new byte; // 10MB 大对象
// 非静态内部类
class InnerClass {
// 内部类隐式持有 OuterClass 引用
}
public InnerClass createInner() {
return new InnerClass();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
InnerClass inner = outer.createInner();
// 置空外部类引用,但 inner 仍持有 outer 引用
outer = null;
// 若 inner 被静态变量/线程长期持有 → outer 对象(含 bigData)无法回收
}
}</pre></div>
<p class="maodian"></p><h2>5、 监听器与回调</h2>
<p>注册了 <strong>监听器 </strong>或 <strong>回调</strong> 后,在对象不再需要时 <strong>没有注销</strong>,导致源对象仍持有监听器的引用(比如 事件监听器、消息队列的消费者等)。</p>
<p><strong>排查工具推荐</strong></p>
<ul><li><strong>MAT(Memory Analyzer Tool):</strong>分析堆 Dump 文件,定位泄漏对象、引用链(谁在持有泄漏对象);</li><li><strong>VisualVM:</strong>JDK 自带工具,监控内存占用趋势,生成堆 Dump,简单排查泄漏。</li></ul>
<p>这里我只列了常见的几种情况,欢迎大家补充其他内存泄漏场景。</p>
<p>到此这篇关于Java导致内存泄漏的多种情况分析的文章就介绍到这了,更多相关java内存泄漏内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>Java SpringBoot内存泄漏问题与解决过程</li><li>Java中的内存泄漏与避免指南</li><li>java内存泄漏排查过程及解决</li><li>避免Java内存泄漏的10个黄金法则详细指南</li><li>Java的内存泄漏和性能瓶颈解读</li><li>避免Java中的内存泄漏的三种方法</li><li>Java中ThreadLocal避免内存泄漏的方法详解</li><li>Java内存泄漏问题排查与解决</li><li>一篇文章带你搞定JAVA内存泄漏</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]