在Java中如何创建一个内存泄漏?
我刚刚参加了一个面试,面试官让我用Java创建一个内存泄漏***。
毫无疑问,我感到相当愚蠢,不知道如何开始创建它。
举个例子呢?
***在纯Java中创建一个真正的内存泄漏(无法通过运行代码访问的对象但仍存储在内存中)的好方法是:
- 应用程序创建一个长时间运行的线程(或使用线程池以更快地泄漏)。
- 线程通过一个(可选自定义的)
ClassLoader加载一个类。 - 该类分配一大块内存(例如
new byte[1000000]),在静态字段中将其强引用存储起来,然后将对自身的引用存储在一个ThreadLocal中。分配额外的内存是可选的(泄漏类实例就足够了),但它会使泄漏工作得更快。 - 应用程序清除对自定义类或它所加载的
ClassLoader的所有引用。 - 重复。
由于Oracle的JDK中ThreadLocal的实现方式,这会创建一个内存泄漏:
- 每个
Thread都有一个私有字段threadLocals,它实际上存储了线程本地值。 - 这个映射中的每个键是对
ThreadLocal对象的弱引用,因此在此ThreadLocal对象被垃圾回收后,它的条目将从映射中删除。 - 但是每个值是强引用,因此当一个值(直接或间接)指向其键的
ThreadLocal对象时,只要线程存在,该对象既不会被垃圾回收也不会从映射中删除。
在这个例子中,强引用链如下所示:
Thread对象 → threadLocals映射 → 示例类实例 → 示例类 → 静态ThreadLocal字段 → ThreadLocal对象。
(ClassLoader在创建泄漏中并不真正发挥作用,只是因为这个额外的引用链使它变得更糟:示例类 → ClassLoader → 它所加载的所有类。在Java 7之前,甚至在许多JVM实现中,类和ClassLoader直接分配到permgen中,并且从未被垃圾回收过。)
这种模式的一个变体是为什么应用程序容器(如Tomcat)可以在重新部署经常使用指向自己的ThreadLocal的应用程序时像筛子一样泄漏内存。这种情况可能有很多微妙的原因,并且通常很难调试和/或修复。
更新:由于很多人一直在问,这里有一些演示这种行为的示例代码。

浙公网安备 33010602011771号