Java 奇怪的JVM线程挂起-故障排除建议?
在对生产环境中的一个jvm挂起问题进行故障排除时,我们遇到了执行以下logger语句的一个线程Java 奇怪的JVM线程挂起-故障排除建议?,java,multithreading,jvm,hung,Java,Multithreading,Jvm,Hung,在对生产环境中的一个jvm挂起问题进行故障排除时,我们遇到了执行以下logger语句的一个线程 logger.debug("Loaded ids as " + ids + "."); 在此步骤挂起,线程状态为runnable。这是一套。还有另一个线程通过倒计时锁存器等待上述线程完成其任务。软件每15分钟进行一次线程转储,两个线程的堆栈跟踪如下所示 Stack trace for [THREAD GROUP: Job_Executor] [THREAD NAME:main-Runner Thre
logger.debug("Loaded ids as " + ids + ".");
在此步骤挂起,线程状态为runnable。这是一套。还有另一个线程通过倒计时锁存器等待上述线程完成其任务。软件每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个线程处于可运行状态,包括上述线程
仅执行collection.toString()时,上述线程24小时处于可运行状态的原因是什么
有关于如何继续的建议吗?这取决于调用的
toString()
方法。我已经看到,当正在构造的字符串对于堆来说太大时,AbstractCollection.toString
就会崩溃。否则,问题可能出现在集合中对象的toString
中
要确定它是哪一个,再进行一些堆栈转储(大约10次)。卡住的螺纹可能通常位于导致问题的toString
中
作为快速修复,请更换
logger.debug("Loaded ids as " + ids + ".");
与
(假设您使用的是slf4j,否则请在框架中查找进行参数化日志记录的适当方法)
如果未启用调试,这将跳过toString
仅执行collection.toString()时,上述线程24小时处于可运行状态的原因是什么
您没有提供足够的信息来诊断问题。我只想挑战您不要假设这里存在JVM问题
如果我们查看AbstractCollection.toString()方法的源代码,我们会看到它在集合中迭代并发出大约“[item0,item1,item2]”。调用每个item.toString()
方法来显示该项
如果应用程序挂起在集合toString()
中,那么我猜集合上的迭代器有问题。如果您的应用程序正在运行,您可以知道这一点——使用接近100%的CPU。可能Set
上的hasNext()
方法总是返回true
如果应用程序挂起实际上位于项.toString()
中,那么我会确保您的项只显示简单字段。小心那些如果被访问就会进行RPC调用的字段,比如延迟加载的ORM包装字段
如果您提供有关所讨论的集合的详细信息,并显示id.toString()
代码,我们可以提供更多帮助
现在听起来这是一组Integer
对象。不知道为什么这会挂起你的应用程序。以下是一些其他想法:
- 您是否以非同步方式访问此集合?是否有多个线程对集合进行了更改,导致集合损坏,从而导致其迭代器旋转?您可以尝试将其包装到一个
Collections.synchronizedSet(…)
中
- 有没有可能
集合
太大了,你的内存快用完了,而程序正在颠簸?但是,这不会挂起应用程序,只会使其缓慢爬行。您将开始看到内存不足异常
- 是否有可能反复调用
toString()
?不过,我想你会在日志中看到这一点
您能给我们展示一下toString()
方法吗?是否存在延迟加载hibernate字段或其他复杂性?我将以此作为建议。感谢您指出。这如何解决toString()
方法的问题?@Gray它没有。但他们说问题出在生产上toString
主要面向开发人员,通常不应该在生产IMHO中调用。真的吗toString()
在我们的环境中一直被调用以进行生产日志记录。呈现视图时,Web应用程序在容器对象上的任何地方都调用toString()
。@artbristol很高兴看到MHO反映在YHO中。许多人认为toString不是一个对象转储程序,而是一个GUI填充程序,从而“赢得”了关于疯狂代码的讨论:(我们使用log4j,这意味着在记录语句之前,我们必须添加一个if检查,以查看日志级别是否为debug。但正如格雷所说,这似乎与问题无关。该软件在流程的进一步运行中运行良好。我们正试图了解上述罕见jvm背后的根本原因是如何挂起的。感谢您的inpu底层集合是一个HashSet。就我所知,如果代码试图对一个延迟加载的集合进行RPC调用,堆栈跟踪是否会表明这一点?我的意思是,它不会显示hibernate代码试图进行数据库调用的跟踪吗?你会这么认为,是的@Andy。我只是看不到HashSet
挂起任何东西,所以假设问题是还有其他事情在进行。啊哈!!@AndyDufresne是否正在对此集合进行同步?是否有可能多个线程正在以非同步方式写入此集合并已将其损坏,从而导致某些内容正在旋转?您可以尝试将其包装到集合中。synchronizedSet(…)@AndyDufresne。谢谢你的想法。以下是我对你提到的上述三点的回应-1.集合没有被多个线程访问。我通过代码确认了这一点。线程转储也会表明如果是这样的话,但是没有痕迹。2.不,集合的大小不是很大。它几乎没有任何元素。另外,由于jvm被挂起超过24小时,这将确认内存没有问题。3.toString()只是在该记录器中调用的。我不认为log4j是
logger.debug("Loaded ids as {}.", ids);