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

73 浏览
0 Comments

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

我刚刚参加了一次面试,被要求使用Java创建一个内存泄漏

不用说,我感觉非常愚蠢,完全不知道如何开始创建一个内存泄漏。

你能给出一个例子吗?

admin 更改状态以发布 2023年5月25日
0
0 Comments

持有对象引用的静态字段[尤其是一个 final 字段]

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设置

0
0 Comments

这是在纯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 → 它加载的所有类。在许多JVM实现中,特别是在Java 7之前,情况甚至更糟,因为类和ClassLoader直接分配到永久代中,根本不会被垃圾收集。)

这种模式的一个变种是,如果你经常重新部署应用程序,并且这些应用程序恰好使用了某些方式指向自身的ThreadLocal,那么应用程序容器(如Tomcat)就会像漏斗一样泄漏内存。这可能会因为许多微妙的原因发生,并且通常很难调试或修复。
更新:由于许多人一直在要求,这里提供一些演示此行为的示例代码

0