找回密码
 立即注册
首页 业界区 业界 Java 哪些情况会导致内存泄漏

Java 哪些情况会导致内存泄漏

寥唏 4 天前
​今天我们来一起聊一聊有哪些情况会导致内存泄漏。
什么是 内存泄漏 呢?
内存泄漏 是指对象 已经不再被程序使用,但因为某些原因 无法被垃圾回收器回收,长期占用内存,最终可能引发 OOM(OutOfMemoryError)。
接下来我们看一下常见的几类内存泄漏场景。
1、生命周期长的集合
将对象放入 静态 或 生命周期很长 的集合(如 public static List list = new ArrayList();),即使后面不再需要,集合仍持有其引用,导致无法GC
2、未关闭的资源
连接、流等资源未调用 close() 方法关闭。这些资源不仅占用内存,还可能占用文件句柄(操作系统分配的唯一标识,凭它,你才能操作文件资源)、网络连接等系统资源。比如 数据库连接、文件流(FileInputStream)、Socket连接 等。
  1. public class FileTest {
  2.     public static void main(String[] args) {
  3.         FileInputStream fis = null;
  4.         try {
  5.             fis = new FileInputStream("test.txt");
  6.             // 读取文件,未调用 fis.close()
  7.         } catch (FileNotFoundException e) {
  8.             e.printStackTrace();
  9.         } finally {
  10.             // 未调用 fis.close() → fis 持有 Native 引用,无法回收
  11.         }
  12.     }
  13. }
复制代码
3、ThreadLocal 使用不当
将对象存入 ThreadLocal 后,未在后续调用 remove() 清理。若线程来自线程池(会复用),其 ThreadLocalMap 中的值会一直存活。
  1. public class ThreadLocalTest {
  2.     private static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
  3.     public static void main(String[] args) {
  4.         // 线程池(核心线程长期存活)
  5.         TThreadPoolExecutor executor = new ThreadPoolExecutor(
  6.                 2,
  7.                 4,
  8.                 10,
  9.                 TimeUnit.SECONDS,
  10.                 new ArrayBlockingQueue<>(100),
  11.                 new ThreadFactoryBuilder().setNameFormat("my-thread-pool-%d").setDaemon(false).setPriority(Thread.NORM_PRIORITY).build(),
  12.                 new ThreadPoolExecutor.AbortPolicy()
  13.         );
  14.         executor.submit(() -> {
  15.             User user = new User("李四", 30);
  16.             userThreadLocal.set(user); // 存储到 ThreadLocal
  17.             // 业务执行完毕,未调用 remove()
  18.             // 核心线程不会销毁,ThreadLocal 仍持有 user 引用
  19.         });
  20.     }
  21. }
复制代码
ps:未进行 remove(),还可能会导致 ThreadLocal 取值串门。
4、内部类与外部类引用
非静态内部类(或匿名类)会 隐式持有 外部类的引用。如果内部类实例生命周期更长(如被缓存或另一个线程引用),会阻止外部类被回收。
  1. public class OuterClass {
  2.     private byte[] bigData = new byte[1024 * 1024 * 10]; // 10MB 大对象
  3.     // 非静态内部类
  4.     class InnerClass {
  5.         // 内部类隐式持有 OuterClass 引用
  6.     }
  7.     public InnerClass createInner() {
  8.         return new InnerClass();
  9.     }
  10.     public static void main(String[] args) {
  11.         OuterClass outer = new OuterClass();
  12.         InnerClass inner = outer.createInner();
  13.         // 置空外部类引用,但 inner 仍持有 outer 引用
  14.         outer = null;
  15.         // 若 inner 被静态变量/线程长期持有 → outer 对象(含 bigData)无法回收
  16.     }
  17. }
复制代码
5、 监听器与回调
注册了 监听器 回调 后,在对象不再需要时 没有注销,导致源对象仍持有监听器的引用(比如 事件监听器、消息队列的消费者等)。
排查工具推荐

  • MAT(Memory Analyzer Tool):分析堆 Dump 文件,定位泄漏对象、引用链(谁在持有泄漏对象);
  • VisualVM:JDK 自带工具,监控内存占用趋势,生成堆 Dump,简单排查泄漏。
这里我只列了常见的几种情况,欢迎大家补充其他内存泄漏场景。
人间非净土,各有千种愁,万般苦。-- 烟沙九洲

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册