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

内容来自 DOC https://q.houxu6.top/?s=在Java中如何创建一个内存泄漏?

我刚刚参加了一个面试,面试官让我用Java创建一个内存泄漏***。

毫无疑问,我感到相当愚蠢,不知道如何开始创建它。

举个例子呢?


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

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

由于Oracle的JDK中ThreadLocal的实现方式,这会创建一个内存泄漏:

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

在这个例子中,强引用链如下所示:

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

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

这种模式的一个变体是为什么应用程序容器(如Tomcat)可以在重新部署经常使用指向自己的ThreadLocal的应用程序时像筛子一样泄漏内存。这种情况可能有很多微妙的原因,并且通常很难调试和/或修复。

更新:由于很多人一直在问,这里有一些演示这种行为的示例代码。

posted @ 2023-10-23 19:59  小满独家  阅读(30)  评论(0)    收藏  举报