当我调用Thread对象的run()时,为什么我的Java程序会泄漏内存?

问题描述:

(Jeopardy风格的问题,我希望答案已经在线,当我有这个问题)当我调用Thread对象的run()时,为什么我的Java程序会泄漏内存?

使用Java 1.4,我有一个方法,我想运行一些线程的时间,但不是在其他人。所以我将它声明为Thread的子类,然后根据我需要的调用start()或run()。

但我发现我的程序会随着时间流逝而泄漏内存。我究竟做错了什么?

这是一个已知的bug在Java 1.4中: http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087

它固定在Java 1.5中,但Sun并不打算将其固定1.4。

问题是,在构建时,Thread被添加到内部线程表中的引用列表中。在start()方法完成之前,它不会从该列表中删除。只要这个参考在那里,它就不会被垃圾收集。

所以,除非你一定要打电话给start()方法,否则不要创建线程。 A Thread对象的run()方法不应该直接调用。

更好的编码方式是实现Runnable接口而不是子类Thread。当你不需要一个线程调用

myRunnable.run(); 

当你需要一个线程:

Thread myThread = new Thread(myRunnable); 
myThread.start(); 
+0

这是一个与实现相关的问题(比如说Sun的JVM),还是它在Java语言规范中的某个地方? – Alexander 2008-09-20 10:35:33

+0

编辑 - 我做了一些更多的调查,这是一个JVM错误 – slim 2008-09-21 10:29:31

让我们看看如果我们能找到更接近问题的核心:

如果你开始你的程序(可以说)1000 x使用开始(),然后1000 x使用run()在一个线程中,做两个松散的内存?如果是这样,那么应该检查你的算法(即对于外部对象,比如你的Runnable中使用的向量)。

如果没有像上面描述的那样的内存泄漏,那么您应该调查有关JVM的线程的启动参数和内存使用情况。

我怀疑构造一个线程或其子类的实例泄漏内存。首先,在Javadocs或Java语言规范中没有提及的种类。其次,我进行了简单的测试,它也表明,没有内存泄漏(至少不是Sun的JDK 1.5.0_05 32位x86的Linux 2.6):

public final class Test { 
    public static final void main(String[] params) throws Exception { 
    final Runtime rt = Runtime.getRuntime(); 
    long i = 0; 
    while(true) { 
     new MyThread().run(); 
     i++; 
     if ((i % 100) == 0) { 
     System.out.println((i/100) + ": " + (rt.freeMemory()/1024/1024) + " " + (rt.totalMemory()/1024/1024)); 
     } 
    } 
    } 

    static class MyThread extends Thread { 
    private final byte[] tmp = new byte[10 * 1024 * 1024]; 

    public void run() { 
     System.out.print("."); 
    } 
    } 
} 

编辑:只是为了总结的想法上面的测试。 Thread的MyThread子类的每个实例都引用其自己的10 MB数组。如果MyThread的实例没有被垃圾收集,那么JVM会很快耗尽内存。但是,运行测试代码表明,不管迄今为止构建的MyThreads的数量是多少,JVM都使用少量的内存。我声称这是因为MyThread的实例被垃圾收集。