问题

我刚接受采访,并被要求用Java创建内存泄漏。毋庸置疑,我对于如何开始创建一个自己而言毫无头绪。

一个例子会是什么?


#1 热门回答(1914 赞)

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

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

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

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

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

更新:由于很多人一直在问它,here's some example code that shows this behavior in action


#2 热门回答(1052 赞)

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

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

长时间调用String.intern()String

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选项可防止未使用的类垃圾回收

SeeIBM jdk settings


#3 热门回答(388 赞)

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

如果你想让这些不好的键/元素挂起来,你可以使用类似的静态字段

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.

原文链接