当前位置: 首页 > 图灵资讯 > 技术篇> Java中有哪些常见的内存泄漏场景?

Java中有哪些常见的内存泄漏场景?

来源:图灵教育
时间:2024-04-15 13:34:09

java 常见的内存泄漏场景包括:持有外部对象引用、静态引用、无效监听器、局部线程变量和循环引用。应用服务器中常见的内存泄漏场景包括:线程保存 servlet 对象引用、静态持有器保持持久连接引用,侦听器未从组件中移除。

Java中有哪些常见的内存泄漏场景?

Java 常见的内存泄漏场景

内存泄漏是软件开发中的一个严重缺陷,随着时间的推移会导致应用程序崩溃或性能下降。以下是 Java 最常见的内存泄漏场景:

1. 引用持有对外部对象的引用

当一个对象持有引用外部对象时,JVM 当外部物体不使用时,不能回收垃圾。例如:

class Outer {
    private Inner inner;

    public Outer() {
        inner = new Inner();  // 持有对 Inner 的引用
    }
}

class Inner {
    // ...
}

登录后复制

2. 静态引用

存储静态变量 JVM 在永久内存中,它们永远不会被垃圾回收。如果引用静态变量持有对象,则该对象不能被垃圾回收。例如:

public class Example {
    private static List<Object> objects = new ArrayList<>();

    public static void main(String[] args) {
        objects.add(new Object());
    }
}

登录后复制

3. 无效监听器

当侦听器不再使用但仍附加到事件源时,内存泄漏就会发生。例如:

import javax.swing.*;

public class ListenerLeak {
    private JButton button;

    public ListenerLeak() {
        button = new JButton();
        button.addActionListener(e -> {
            // ...
        });
    }

    // 忘记从按钮中移除监听器
}

登录后复制

4. 局部线程变量

存储在每个线程中的线程局部变量 (TLS) 在中间,只要线程处于活动状态,它们就不会被垃圾回收。如果您在已完成的线程中使用局部线程变量,可能会导致内存泄漏。例如:

public class ThreadLocalLeak {
    private static ThreadLocal<Object> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            threadLocal.set(new Object());  // 在线程中设置值
        });
        thread.start();
        thread.interrupt();  // 中断线程

        // 未清理局部线程存储
    }
}

登录后复制

5. 循环引用

当两个或两个以上的对象相互引用时,就会发生循环引用。这将导致循环引用。 JVM 无法识别它们已不再使用,导致内存泄漏。例如:

public class CycleReference {
    private CycleReference other;

    public CycleReference() {
        other = new CycleReference();
        other.other = this;  // 循环引用
    }
}

登录后复制

实战案例

应用服务器中的内存泄漏

以下是应用服务器中典型的内存泄漏场景:

  1. 线程保存对 servlet 即使是引用对象 servlet 已完成。
  2. 静态持有器 (如数据库连接池) 即使不再需要这些连接,也要保持引用持久连接。
  3. 侦听器未从组件中移除,导致保留了侦听器的引用。

通过了解常见的内存泄漏场景,并采用适当的编码实践,可以减少 Java 为了保证其稳定性和性能,应用程序中内存泄漏的风险。

Java中常见的内存泄漏场景有哪些?详情请关注图灵教育其他相关文章!