问题描述
这是在纯 Java 中创建真正的内存泄漏(运行代码无法访问但仍存储在内存中的对象)的好方法:
- 应用程序创建一个长时间运行的线程(或使用线程池更快地泄漏)。
- 线程通过(可选自定义)加载一个类
ClassLoader
。 - 该类分配一大块内存(例如
new byte[1000000]
),将对其的强引用存储在静态字段中,然后将对自身的引用存储在ThreadLocal
. 分配额外的内存是可选的(泄漏类实例就足够了),但它会使泄漏工作得更快。 - 应用程序清除所有对自定义类或
ClassLoader
从其加载的类的引用。 - 重复。
由于ThreadLocal
在 Oracle 的 JDK 中实现的方式,这会产生内存泄漏:
- 每个
Thread
都有一个私有字段threadLocals
,它实际上存储线程本地值。 - 此映射中的每个键都是对对象的弱引用
ThreadLocal
,因此在该ThreadLocal
对象被垃圾回收后,它的条目将从映射中删除。 - 但是每个值都是一个强引用,因此当一个值(直接或间接)指向作为
ThreadLocal
其key的对象时,只要线程存在,该对象就不会被垃圾收集或从映射中删除。
在此示例中,强引用链如下所示:
Thread
对象→threadLocals
映射→示例类的实例→示例类→静态ThreadLocal
字段→ThreadLocal
对象。
(ClassLoader
在创建泄漏中并没有真正发挥作用,它只是因为这个额外的引用链而使泄漏变得更糟:示例类→→ClassLoader
它已加载的所有类。在许多 JVM 实现中甚至更糟,尤其是在之前Java 7,因为 classes 和ClassLoader
s 被直接分配到 permgen 并且根本没有被垃圾回收。)
这种模式的一个变体是,如果您经常重新部署恰好使用ThreadLocal
以某种方式指向自身的 s 的应用程序,应用程序容器(如 Tomcat)可能会像筛子一样泄漏内存。这可能由于许多微妙的原因而发生,并且通常难以调试和/或修复。
最终
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
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();
}
,例如通过本机方法分配的内存。
在 Web 应用程序中,一些对象存储在应用程序范围内,直到应用程序被显式停止或删除。
getServletContext().setAttribute("SOME_MAP", map);
,例如noclassgc
IBM JDK 上阻止未使用的类垃圾收集的选项
解决方法
我刚刚接受了一次面试,我被要求用 Java创建内存泄漏。
不用说,我什至不知道如何开始创建一个
能举一个例子吗?