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

2022-08-31 01:22:46

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

毋庸置疑,我觉得自己很愚蠢,不知道如何开始创建一个。

举个例子吗?


答案 1

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

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

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

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

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

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

(在创建泄漏方面并没有真正发挥作用,它只是由于这个额外的引用链而使泄漏变得更糟:示例类→ →它加载的所有类。在许多JVM实现中,情况更糟,特别是在Java 7之前,因为类和类被直接分配到permgen中,并且从未被垃圾回收。ClassLoaderClassLoaderClassLoader

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

更新:由于很多人一直在要求它,这里有一些示例代码可以显示这种行为的实际效果


答案 2

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

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

在冗长的字符串上调用 String.intern()

String str = readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

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

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