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

Posted

技术标签:

【中文标题】当我在 Thread 对象上调用 run() 时,为啥我的 Java 程序会泄漏内存?【英文标题】:Why is my Java program leaking memory when I call run() on a Thread object?当我在 Thread 对象上调用 run() 时,为什么我的 Java 程序会泄漏内存? 【发布时间】:2010-09-11 14:11:45 【问题描述】:

(危险风格的问题,我希望在我遇到这个问题时答案已经在线)

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

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

【问题讨论】:

【参考方案1】:

这是 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() 方法,否则永远不要创建一个线程。不应直接调用Thread 对象的run() 方法。

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

myRunnable.run();

当你确实需要线程时:

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

【讨论】:

这是一个依赖于实现的问题(例如,Sun 的 JVM),还是在 Java 语言规范中的某个地方进行了概述? 已编辑 - 我做了更多调查,这是一个 JVM 错误【参考方案2】:

我怀疑构造 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(".");
    
  

编辑:只是总结一下上面测试的想法。 Thread 的 MyThread 子类的每个实例都引用它自己的 10 MB 数组。如果 MyThread 的实例没有被垃圾回收,JVM 会很快耗尽内存。但是,运行测试代码表明 JVM 使用的内存量很小,与目前构建的 MyThreads 数量无关。我声称这是因为 MyThread 的实例是垃圾收集的。

【讨论】:

啊哈 - 做了更多研究,发现这是一个在 Java 1.5 中得到修复的错误【参考方案3】:

让我们看看能否更接近问题的核心:

如果您使用 start() 启动程序(假设)1000 x,然后在线程中使用 run() 启动 1000 x,是否都会释放内存?如果是这样,则应检查您的算法(即,检查外部对象,例如 Runnable 中使用的向量)。

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

【讨论】:

以上是关于当我在 Thread 对象上调用 run() 时,为啥我的 Java 程序会泄漏内存?的主要内容,如果未能解决你的问题,请参考以下文章

创建多线程方式一(继承Thread类)

线程--继承Thread

等待 std::thread 完成

Java创建多线程方式之一:继承Thread类

Thread常用方法

当我在 NULL 对象指针上调用成员函数时会发生啥? [复制]