我刚刚接受了一次采访,被要求用Java创建内存泄漏。

不用说,我觉得自己很傻,不知道如何开始创作。

什么样的例子?


当前回答

以在任何servlet容器(Tomcat、Jetty、GlassFish等)中运行的任何web应用程序为例。连续重新部署应用程序10或20次(只需在服务器的autodeploy目录中触摸WAR即可)。

除非有人真的测试过,否则在重新部署几次之后,很可能会出现OutOfMemoryError,因为应用程序没有注意自己的清理。通过此测试,您甚至可以在服务器中发现错误。

问题是,容器的生存期比应用程序的生存期长。您必须确保容器对应用程序的对象或类的所有引用都可以被垃圾收集。

如果只有一个引用在web应用程序的取消部署后仍然存在,则相应的类加载器以及web应用程序中的所有类都不能被垃圾收集。

应用程序启动的线程、ThreadLocal变量、日志附加器是导致类加载器泄漏的常见原因。

其他回答

从finalize方法引发未处理的异常。

我在Java中看到的大多数内存泄漏都与进程不同步有关。

进程A通过TCP与B对话,并告诉进程B创建一些东西。B向资源发出一个ID,比如432423,A将其存储在一个对象中,并在与B对话时使用。在某些情况下,A中的对象会被垃圾收集回收(可能是由于错误),但A从不告诉B这一点(可能是另一个错误)。

现在A不再拥有它在B的RAM中创建的对象的ID,B也不知道A不再引用该对象。实际上,对象是泄漏的。

JDK 1.7之前内存泄漏的实时示例:

假设您读取了一个包含1000行文本的文件,并将其保存在String对象中:

String fileText = 1000 characters from file
fileText = fileText.subString(900, fileText.length());

在上面的代码中,我最初读取了1000个字符,然后执行了子字符串,只获得最后100个字符。现在,fileText应该只引用100个字符,所有其他字符都应该被垃圾收集,因为我丢失了引用,但是在JDK1.7之前,substring函数间接引用了最后100个字符的原始字符串,并阻止了整个字符串的垃圾收集,而整个1000个字符将一直保存在内存中,直到您丢失了对子字符串的引用。

您可以创建一个类似于上述的内存泄漏示例。

Java1.6中的String.substring方法会造成内存泄漏。这篇博文解释了这一点:

SubString方法在Java中的工作原理-JDK1.7中修复了内存泄漏

这很简单:

Object[] o = new Object[]{};
while(true) {
    o = new Object[]{o};
}