奇怪的JVM线程挂起 - 建议排除故障?

时间:2012-10-09 13:40:42

标签: java multithreading jvm hung

在对我们的生产环境中的一个jvm挂起问题进行故障排除时,我们遇到了执行以下记录器语句的一个线程

logger.debug("Loaded ids as " + ids + ".");
在此步骤中挂起

,线程状态为runnable。这里的ids是一套。还有另一个线程通过倒计时锁存器等待上述线程完成其任务。该软件每隔15分钟进行一次线程转储,两个线程的堆栈跟踪如下所示

Stack trace for [THREAD GROUP: Job_Executor] [THREAD NAME:main-Runner Thread][THREAD STATE: WAITING]
    ...sun.misc.Unsafe.park(Native Method)
    ...java.util.concurrent.locks.LockSupport.park(Unknown Source)
    ...java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
    ...java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(Unknown Source)
    ...java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
    ...java.util.concurrent.CountDownLatch.await(Unknown Source)
    ...com.runner.MainRunner.stopThread(MainRunnerRunner.java:1334)


Stack trace for [THREAD GROUP: Job_Executor] [THREAD NAME:task executor][THREAD STATE: RUNNABLE]    
    ...java.util.AbstractCollection.toString(Unknown Source)           
    ...java.lang.String.valueOf(Unknown Source)      
    ...java.lang.StringBuilder.append(Unknown Source)    
    ...com.runner.CriticalTaskExecutor.loadByIds(CriticalTaskExecutor.java:143)

这个jvm已经挂了将近24小时,最后我们不得不杀死它继续前进。线程转储表明有43个线程处于RUNNABLE状态,包括上面的线程。

上面的线程在执行collection.toString()的24小时内处于RUNNABLE状态的原因是什么?

有关如何进行的任何建议?

2 个答案:

答案 0 :(得分:1)

  

上述线程在执行collection.toString()的24小时内处于RUNNABLE状态的原因是什么?

您没有提供足够的信息来诊断问题。我只会挑战你,不要假设这里有JVM问题。

如果我们查看AbstractCollection.toString()方法的来源,我们会看到它遍历集合并吐出大约“[item0,item1,item2]”。调用每个item.toString()方法来显示项目。

如果应用程序挂起在集合toString()中,那么我的猜测是集合上的迭代器存在一些问题。如果您的应用程序正在旋转,您可以告诉它 - 使用接近100%的CPU。也许hasNext()上的Set方法总是返回true

如果应用程序在item.toString()内部实际挂起,那么我会确保您的项目只显示简单字段。注意如果访问的字段使RPC调用像延迟加载的ORM包装字段。

如果您提供有关Set的详细信息并显示id.toString()代码,我们可以提供更多帮助。

现在听起来这是一组Integer个对象。不知道为什么会挂起你的应用程序。以下是其他一些想法:

  • 您是否以非同步方式访问此集合?多个线程是否可以对集合进行更改以使其损坏导致其迭代器旋转?您可以尝试将其包装在Collections.synchronizedSet(...)
  • Set 巨大的任何可能性,你正在接近内存不足并且程序正在颠簸?这不会挂起您的应用程序,但只是慢慢爬行。你会开始看到内存异常。
  • 是否有可能一次又一次地调用toString()?我假设你会在日志中看到它。

答案 1 :(得分:0)

这取决于被调用的toString()方法。当构造的AbstractCollection.toString对于堆太大时,我已经看到String倒下了。否则,问题可能出在集合中对象的toString

要确定它是哪一个,请多花一些堆栈(10个左右)。卡住的线程可能通常位于导致问题的toString

作为快速解决方案,请替换

logger.debug("Loaded ids as " + ids + ".");

logger.debug("Loaded ids as {}.", ids);

(假设你正在使用slf4j,否则在你的框架中查找适当的方法来进行参数化日志记录)。

如果未启用调试,则会跳过toString。