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

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

什么样的例子?


当前回答

我认为,一个有效的例子可能是在线程集中的环境中使用ThreadLocal变量。

例如,使用Servlet中的ThreadLocal变量与其他web组件通信,让容器创建线程,并在池中维护空闲线程。ThreadLocal变量如果没有正确清理,将一直存在,直到同一个web组件覆盖它们的值。

当然,一旦确定,问题很容易解决。

其他回答

我最近遇到了由log4j引起的内存泄漏情况。

Log4j有一种称为嵌套诊断上下文(NDC)的机制,它是一种区分不同来源的交织日志输出的工具。NDC工作的粒度是线程,因此它区分不同线程的日志输出。

为了存储线程特定的标记,log4j的NDC类使用一个Hashtable,该Hashtable由thread对象本身(而不是线程id)键控,因此直到NDC标记保留在内存中,挂在线程对象上的所有对象也保留在内存。在我们的web应用程序中,我们使用NDC标记带有请求id的登录,以将日志与单个请求区分开来。将NDC标记与线程关联的容器在返回请求响应时也会将其删除。在处理请求的过程中,产生了一个子线程,类似于以下代码:

pubclic class RequestProcessor {
    private static final Logger logger = Logger.getLogger(RequestProcessor.class);
    public void doSomething()  {
        ....
        final List<String> hugeList = new ArrayList<String>(10000);
        new Thread() {
           public void run() {
               logger.info("Child thread spawned")
               for(String s:hugeList) {
                   ....
               }
           }
        }.start();
    }
}    

因此,NDC上下文与派生的内联线程相关联。这个NDC上下文的关键线程对象是一个内联线程,它挂着hugeList对象。因此,即使线程完成了它正在做的事情,对hugeList的引用也会被NDC上下文Hastable保持活动状态,从而导致内存泄漏。

创建一个只包含while true循环的JNI函数,并用另一个线程的大型对象调用它。GC不太喜欢JNI,并且会将对象永久保存在内存中。

您可以尝试使用while循环使许多缓冲的读取器尝试同时打开同一个文件,条件决不为false。最上面的樱桃是这些永远不会关闭。

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

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

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

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

正如许多人所建议的那样,资源泄漏很容易造成,就像JDBC示例一样。实际的内存泄漏有点困难——尤其是如果您不依赖JVM中的碎片来为您进行泄漏。。。

创建占地面积非常大的对象,然后无法访问这些对象的想法也不是真正的内存泄漏。如果没有东西可以访问它,那么它将被垃圾收集,如果有东西可以访问,那么它就不是泄漏。。。

然而,一种曾经有效的方法——我不知道它是否仍然有效——是有一条三深的环形链。正如在对象A中有对对象B的引用,对象B有对对象C的引用,而对象C有对对象A的引用。GC足够聪明,知道如果A和B不能被任何其他对象访问,但不能处理三方链,则可以安全地收集两个深链(如在A<-->B中)。。。