java—如何在不重新启动应用程序的情况下获取StackOverflowerr的完整stacktrace

mpbci0fu  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(511)

我目前有一个正在运行的java应用程序,它有一个bug。我不知道如何完全复制它,直到现在才发生了几个星期。当它发生一次时,我可以一遍又一遍地重复它,直到我重新启动应用程序。这个错误导致了一个堆栈溢出错误,因为递归,我不知道这是怎么发生的。打印的堆栈跟踪 StackOverflowError 这是没有帮助的,因为它只包含重复部分,而不包含更多的初始部分,因为jvm对stacktrace条目有限制。这个 -XX:MaxJavaStackTraceDepth=... 可用于设置此限制,如下所述。问题是我想我必须重新启动我的应用程序才能添加这个标志。但是如果我这么做了,我就不能再复制这个错误了。有没有什么解决方案,我可以得到完整的stacktrace或设置这个标志,而不重新启动应用程序?

kjthegm6

kjthegm61#

我至少知道两种解决办法。
创建热点服务性代理工具以查找 MaxJavaStackTraceDepth 变量,然后使用操作系统特定的机制更新进程的内存。
附加一个截获 StackOverflowErrors 从代理那里打印出一个堆栈跟踪。
以下是第一个解决方案的代码(可能更短):

import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

import java.io.IOException;
import java.io.RandomAccessFile;

public class ChangeVMFlag extends Tool {
    private static String pid;

    @Override
    public void run() {
        Address addr = VM.getVM().getCommandLineFlag("MaxJavaStackTraceDepth").getAddress();
        long addrValue = VM.getVM().getDebugger().getAddressValue(addr);

        try (RandomAccessFile raf = new RandomAccessFile("/proc/" + pid + "/mem", "rw")) {
            raf.seek(addrValue);
            raf.writeInt(Integer.reverseBytes(1_000_000));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        pid = args[0];
        new ChangeVMFlag().execute(new String[]{pid});
    }
}

此工具可更改 MaxJavaStackTraceDepth 在目标进程中达到100万。
注意:它使用特定于linux的 /proc api写入目标进程的内存。其他操作系统有不同的接口。
如何跑步
在jdk 8上

java -cp .:$JAVA_HOME/lib/sa-jdi.jar ChangeVMFlag <pid>

在jdk 9上+

java --add-modules=jdk.hotspot.agent \
     --add-exports jdk.hotspot.agent/sun.jvm.hotspot.tools=ALL-UNNAMED \
     --add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED \
     --add-exports jdk.hotspot.agent/sun.jvm.hotspot.debugger=ALL-UNNAMED \
     ChangeVMFlag <pid>

相关问题