协慌网

登录 贡献 社区

使用 Java 创建内存泄漏

我刚接受采访,并被要求用 Java 创建内存泄漏。毋庸置疑,我觉得自己很傻,甚至不知道如何开始创建一个。

一个例子是什么?

答案

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

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

这是有效的,因为 ThreadLocal 保留对该对象的引用,该对象保持对其 Class 的引用,而 Class 又保持对其 ClassLoader 的引用。反过来,ClassLoader 保持对它已加载的所有类的引用。

(在许多 JVM 实现中,尤其是在 Java 7 之前,情况更糟,因为 Classes 和 ClassLoader 直接分配到 permgen 并且根本就没有 GC。但是,无论 JVM 如何处理类卸载,ThreadLocal 仍然会阻止被回收的类对象。)

此模式的一个变体是,如果您经常重新部署碰巧以任何方式使用 ThreadLocals 的应用程序,那么应用程序容器(如 Tomcat)可能会像筛子那样泄漏内存。 (由于应用程序容器使用了所描述的线程,每次重新部署应用程序时都会使用新的 ClassLoader。)

更新 :由于很多人不断要求它, 这里有一些示例代码显示了这种行为

静态字段保持对象引用 [esp final field]

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

在冗长的 String 上调用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 设置

一个简单的事情是使用具有不正确(或不存在) hashCode()equals()的 HashSet,然后继续添加 “重复”。而不是忽略重复,它只会增长,你将无法删除它们。

如果你想让这些坏键 / 元素闲逛,你可以使用静态字段

class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.