jstack -F对运行中的Java进程有什么影响?

33

我正在尝试诊断一个问题,即我正在使用的Java Web应用程序 (Jenkins) 变得无响应。 如果我不带-F标志运行 jstack ,它不会给我任何结果,但如果我加上 -F 标志来强制进行线程转储,则不仅会得到结果,而且该应用程序开始响应并继续运行,直到最终再次停止响应。

jstack -F 标志会对正在运行的JVM产生何种影响,导致一个不响应的应用程序重新开始响应呢?


2
仅仅是猜测,但这可能会导致虚假唤醒或类似的情况发生。 - Jeffrey
为了确定问题的原因,我会尝试使用jdb。这样当应用程序无响应时,您就可以附加并调查原因。 - dan
2个回答

16
您可以在此处查看jstack的源代码。 -F参数会更改jstack连接到jvm的方式。 使用-F(或-m),JStack使用Java调试器接口连接到jvm。 如果指定了pid,则JStack使用SA PID Attaching Connector进行连接,该连接器指出:

需要调试的进程不需要以调试模式启动(即使用-agentlib:jdwp或-Xrunjdwp)。 进程可以是挂起状态。

我不知道为什么它会导致无响应的应用程序开始重新响应,但上面的链接也说:

当此连接器附加时,进程被暂停,当此连接器分离时,进程恢复。

这可能会产生影响。


PID附加机制不需要被检查的进程进行协作 - 它只是检查内存结构并尝试弄清楚可用的内容。它可能提供的信息比礼貌地询问要少。 - ddimitrov
好的,我明白为什么在执行 jstack -F 命令后能够更好地工作。 - BendaThierry.com
一个进程被挂起并唤醒后,会发生什么变化,导致应用程序重新变得响应? - Jordan Bentley
@JordanBentley 如果不知道应用程序一开始为什么无响应,很难知道原因。是gc'ing、交换出去还是机器本身过载了? - sbridges
大多数情况下,当我在无响应时运行jmap -heap时,它会显示eden空间已满,因此我认为正在进行GC。我打开了并发GC,但没有任何区别。机器不可能超载,它是一台强大的服务器,除了Jenkins之外没有其他东西在运行,即使在其下没有任何构建运行时,问题仍然会发生。我的下一步将尝试使用不同的JVM,但我仍然想知道jstack挂起进程如何改变它的执行方式,以满足自己的好奇心。 - Jordan Bentley

11

jstack -F -l pid 类似于(假设工作目录为 JAVA_HOME)

bin/java -Dsun.jvm.hotspot.debugger.useWindbgDebugger  -Dsun.jvm.hotspot.debugger.useProcDebugger  -cp lib/sa-jdi.jar;lib/tools.jar  sun.tools.jstack.JStack -F -l pid

并且在sun.tools.jstack.JStack代码中。

   if (arg.equals("-F")) {
       useSA = true;
   }
   .....
   // now execute using the SA JStack tool or the built-in thread dumper
   if (useSA) {
       // parameters (<pid> or <exe> <core>
       ...
       runJStackTool(mixed, locks, params);
   } else {
       // pass -l to thread dump operation to get extra lock info
       String pid = args[optionCount];
        ...
       runThreadDump(pid, params);
    }

由于传入了-F参数,runJStackTool被调用以加载sun.jvm.hotspot.tools.JStack,这与直接调用的效果相同。

bin\java -Dsun.jvm.hotspot.debugger.useWindbgDebugger  -Dsun.jvm.hotspot.debugger.useProcDebugger  -cp lib/sa-jdi.jar;lib/tools.jar  sun.jvm.hotspot.tools.JStack pid

而sun.jvm.hotspot.tools.JStack将调用sun.jvm.hotspot.bugspot.BugSpotAgent 的 attach -> go -> setupVM 方法。

也许下面的代码就是这个神奇的地方

       jvmdi = new ServiceabilityAgentJVMDIModule(debugger, saLibNames);
       if (jvmdi.canAttach()) {
           jvmdi.attach();
           jvmdi.setCommandTimeout(6000);
           debugPrintln("Attached to Serviceability Agent's JVMDI module.");
           // Jog VM to suspended point with JVMDI module
           resume();
           suspendJava();
           suspend();
           debugPrintln("Suspended all Java threads.");
       }

它将暂停目标进程中的所有Java线程。如果您的应用程序因线程饥饿而挂起,则调用suspend方法可能会缓解这种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接