3

我得到了我的一个进程的线程转储。它有一堆这样的线程。我猜他们保留了一堆记忆,所以我得到了OOM。

"Thread-8264" prio=6 tid=0x4c94ac00 nid=0xf3c runnable [0x4fe7f000]
   java.lang.Thread.State: RUNNABLE
    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:223)
    - locked <0x0c9bc640> (a java.util.zip.Inflater)
    at org.apache.commons.compress.archivers.zip.ZipArchiveInputStream.read(ZipArchiveInputStream.java:235)
    at com.my.ZipExtractorCommonsCompress.extract(ZipExtractorCommonsCompress.java:48)
    at com.my.CustomThreadedExtractorWrapper$ExtractionThread.run(CustomThreadedExtractorWrapper.java:151)

   Locked ownable synchronizers:
    - None

"Thread-8241" prio=6 tid=0x4c94a400 nid=0xb8c runnable [0x4faef000]
   java.lang.Thread.State: RUNNABLE
    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:223)
    - locked <0x0c36b808> (a java.util.zip.Inflater)
    at org.apache.commons.compress.archivers.zip.ZipArchiveInputStream.read(ZipArchiveInputStream.java:235)
    at com.my.ZipExtractorCommonsCompress.extract(ZipExtractorCommonsCompress.java:48)
    at com.my.CustomThreadedExtractorWrapper$ExtractionThread.run(CustomThreadedExtractorWrapper.java:151)

   Locked ownable synchronizers:
    - None

我试图找出它是如何达到这种情况的。CustomThreadedExtractorWrapper 是一个包装类,它触发一个线程来完成一些工作(ExtractionThread,它使用 ZipExtractorCommonsCompress 从压缩流中提取 zip 内容)。如果任务耗时过长,ExtractionThread.interrupt()则调用取消操作。

我可以在我的日志中看到取消发生了 25 次。我在我的转储中看到了其中的 21 个线程。我的问题:

  1. 这些线程的状态是什么?活着还跑?以某种方式被阻止?
  2. 他们显然没有死于 .interrupt() 吗?有没有确定的方法可以真正杀死一个线程?
  3. 堆栈跟踪中“锁定”的真正含义是什么?

Inflater.java 中的第 223 行是:

public synchronized int inflate(byte[] b, int off, int len) {
    ...
    //return is line 223
    return inflateBytes(b, off, len);                          
}
4

4 回答 4

3

“锁定”意味着他们拥有监视器;即方法为synchronized,线程转储显示执行同步的实例的地址。

您可以尝试使用 杀死一个线程Thread.stop(),但该线程可能会拒绝,而且它本质上是不安全的、不推荐使用的并且非常糟糕。不要做。此外,我不确定当线程处于本机方法时它是否有效,就像这里的情况一样。

Thread.interrupt()轻推目标线程。线程将在下一次显式查看中断标志或执行一些潜在的阻塞操作(I/O 或wait())时注意到它。线程可能会捕获异常并忽略它。

您的线程是“可运行的”:它们没有被阻塞。Inflater.inflate()无论如何都不是阻塞功能;它只执行内存计算。本机实现中可能存在错误(Inflater.inflateBytes(),但这不太可能,因为这依赖于Zlib,这是一段非常稳定的代码)。更合理的是,其中一个调用者(例如您的ZipExtractorCommonsCompress班级)陷入了一个循环,在该循环中它要求 Zip 提取器再处理零个字节,并且不明白它应该在重试之前等待更多数据。

于 2010-03-30T12:51:04.823 回答
2
  1. 所有这些线程都处于可运行状态

  2. 中断不会杀死线程......它只是一个指示线程是否被中断的标志,方法 sleep , wait 并且都会在线程中断时抛出 InteruptedException 。如果要在中断时停止线程,请检查方法和 isInterupted() 并完成该线程中的所有工作

  3. 锁定表示一个特定对象被该线程锁定

于 2010-03-30T12:46:26.830 回答
0

有没有确定的方法可以真正杀死一个线程?

不,那里没有。

正如您所观察到Thread.interrupt()的,建议线程停止,但它可能不会注意到,或者它可能决定不注意。

唯一的其他选择是Thread.stop(), 并且已被弃用,因为它会严重破坏应用程序的稳定性。具体来说,Thread.stop()导致所有线程的锁都被释放,而没有任何保证锁所保护的状态处于可见的适合状态。同时,意外异常意味着被中断的方法stop()没有机会(例如)notify()其他正在等待的线程。最后,线程有可能捕获(而不是重新抛出)ThreadDeath异常对象,导致stop()线程根本不停止。

简而言之Thread.stop(),打电话是一个非常非常糟糕的主意。

于 2010-03-30T13:10:47.850 回答
0

如果可能的话,我建议您尝试使用 jvisualvm(它位于 JDK 安装的 bin 文件夹中)。它可以连接到任何本地 java 进程,并为您提供线程信息和内存信息(使用情况、分配的对象等)。该界面比控制台转储更容易解释。对于您的问题:

Thread.State 的定义在 API 中:

NEW - A thread that has not yet started is in this state.
RUNNABLE - A thread executing in the Java virtual machine is in this state.
BLOCKED - A thread that is blocked waiting for a monitor lock is in this state.
WAITING - A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
TIMED_WAITING - A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
TERMINATED - A thread that has exited is in this state.

所以上面跟踪中的两个线程是活动的(注意可运行意味着它们可以运行,不一定是它们正在运行,即操作系统调度程序可能在另一个线程正在执行时让它们暂停)。

“杀死”线程的可能方法:

  • 有一个未捕获的异常
  • 调用线程的 stop()
  • 让线程正常完成执行(即从run() 退出)。

对于您的第三个问题,我不确定,但我相信这是对线程持有的内部锁的引用。

于 2010-03-30T12:48:02.340 回答