当我在Thread对象上调用run()时,为什么Java程序会泄漏内存?
(Jeopardy风格的问题,希望当我遇到此问题时,答案已经在线)
使用Java 1.4,我有一种方法希望在某些时候作为线程运行,而在其他时候则不想。因此,我将其声明为Thread的子类,然后根据需要将其称为start()或者run()。
但是我发现我的程序会随着时间的流逝而泄漏内存。我究竟做错了什么?
解决方案
这是Java 1.4中的一个已知错误:
http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087
它在Java 1.5中已修复,但Sun并不打算在1.4中对其进行修复。
问题是,在构造时,将"线程"添加到内部线程表中的引用列表中。直到它的start()方法完成后,它才会从该列表中删除。只要存在该引用,就不会收集垃圾。
因此,除非我们肯定要调用它的start()
方法,否则请不要创建线程。线程对象的run()方法不应直接调用。
一种更好的编码方法是实现Runnable接口,而不是子类Thread。当我们不需要线程时,请致电
myRunnable.run();
当我们确实需要线程时:
Thread myThread = new Thread(myRunnable); myThread.start();
让我们看看是否可以更进一步地解决问题的核心:
如果在线程中使用start()启动程序(假设)为1000 x,然后在线程中使用run()启动程序为1000 x,这两个内存是否都松散了?如果是这样,则应检查算法(即是否有外部对象,例如Runnable中使用的Vector)。
如果没有如上所述的此类内存泄漏,则应调查有关JVM的线程的启动参数和内存使用情况。
我怀疑构造Thread实例或者其子类会泄漏内存。首先,在Javadocs或者Java语言规范中没有提到任何种类的东西。其次,我运行了一个简单的测试,它还显示没有内存泄漏(至少不是在32位x86 Linux 2.6上的Sun的JDK 1.5.0_05上泄漏):
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("."); } } }
编辑:只是总结以上测试的想法。线程的MyThread子类的每个实例都引用其自己的10 MB数组。如果未对MyThread的实例进行垃圾回收,则JVM将很快耗尽内存。但是,运行测试代码表明,无论到目前为止构建的MyThreads数量如何,JVM都使用少量恒定的内存。我声称这是因为MyThread的实例被垃圾收集了。