如何在 Java 中创建内存泄漏?

新手上路,请多包涵

我刚刚接受了一次面试,我被要求用 Java 创建 _内存泄漏_。

不用说,我什至不知道如何开始创建一个,我感到很愚蠢。

一个例子是什么?

原文由 MatBanik 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 765
2 个回答

这是在纯 Java 中创建真正的内存泄漏(运行代码无法访问但仍存储在内存中的对象)的好方法:

  1. 应用程序创建一个长时间运行的线程(或使用线程池更快地泄漏)。
  2. 线程通过(可选自定义) ClassLoader 加载一个类。
  3. 该类分配一大块内存(例如 new byte[1000000] ),在静态字段中存储对它的强引用,然后在 ThreadLocal 中存储对自身的引用。分配额外的内存是可选的(泄漏类实例就足够了),但它会使泄漏工作得更快。
  4. 应用程序清除所有对自定义类或 ClassLoader 加载它的引用。
  5. 重复。

由于 ThreadLocal 在 Oracle 的 JDK 中实现的方式,这会造成内存泄漏:

  • 每个 Thread 都有一个私有字段 threadLocals ,它实际上存储线程本地值。
  • 此映射中的每个 都是对 ThreadLocal 对象的弱引用,因此在 ThreadLocal 对象被垃圾收集之后,其条目将从映射中删除。
  • 但是每个 都是一个强引用,所以当一个值(直接或间接)指向 --- ThreadLocal 对象时,只要线程生活。

在此示例中,强引用链如下所示:

Thread 对象 → threadLocals 映射 → 示例类的实例 → 示例类 → 静态 ThreadLocal 字段 → ThreadLocal 对象。

ClassLoader 并没有真正在创建泄漏中发挥作用,它只是因为这个额外的引用链而使泄漏变得更糟:示例类 → ClassLoader → 它拥有的所有类在许多 JVM 实现中甚至更糟,尤其是在 Java 7 之前,因为类和 ClassLoader s 被直接分配到 permgen 并且根本没有被垃圾收集。)

这种模式的一个变体是,如果您经常重新部署碰巧使用 ThreadLocal 的应用程序,应用程序容器(如 Tomcat)可能会像筛子一样泄漏内存,而这些应用程序在某种程度上指向了它们自己。这可能由于许多微妙的原因而发生,并且通常难以调试和/或修复。

更新:由于很多人一直要求它, 这里有一些示例代码显示了这种行为

原文由 Daniel Pryden 发布,翻译遵循 CC BY-SA 4.0 许可协议

持有对象引用的静态字段[尤其是 最终 字段]

 class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

(未关闭)打开流(文件、网络等)

 try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStackTrace();
}

未关闭的连接

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStackTrace();
}

JVM 的垃圾收集器无法访问的区域,例如通过本机方法分配的内存。

在 Web 应用程序中,一些对象存储在应用程序范围内,直到应用程序被显式停止或删除。

 getServletContext().setAttribute("SOME_MAP", map);

不正确或不适当的 JVM 选项,例如 IBM JDK 上的 noclassgc 选项可防止未使用的类垃圾回收

请参阅 IBM JDK 设置

原文由 Prashant Bhate 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题