问题描述
我目前有一个正在运行的Java应用程序,该应用程序有一个错误.我不知道该如何完全复制它,直到现在为止都没有发生.当它发生一次时,我可以轻松地一遍又一遍地重现它,直到重新启动应用程序为止.该错误由于递归而导致StackOverflowError,我不知道这是怎么发生的. StackOverflowError
的打印堆栈跟踪没有帮助,因为它仅包含重复部分,而不包含更有趣的初始部分,因为JVM对堆栈跟踪条目有限制. -XX:MaxJavaStackTraceDepth=...
可用于设置此限制,如此处所述.问题是我认为我必须重新启动应用程序才能添加此标志.但是,如果这样做,我将无法再复制该错误.有什么解决方案可以在不重新启动应用程序的情况下如何获取完整的堆栈跟踪信息或设置此标志?
I currently hava a running Java application, which has a bug. I don't know how to fully reproduce it and it didn't happen for weeks until now. When it occurs one times, I can reproduce it over and over again easily until I restart the application. The bug causes a StackOverflowError because of a recursion and I don't know how this happens. The printed stacktrace of the StackOverflowError
isn't helpful because it contains only the repeating part, but not the more insteresting initial part, because the JVM has a limit for stacktrace entries. The -XX:MaxJavaStackTraceDepth=...
can be used to set this limit as explained here. The problem is that I think I have to restart my application in order to add this flag. But if I do so, I won't be able to reproduce the bug anymore. Is there any solution how I can get the full stacktrace or set this flag without restarting the application?
推荐答案
我至少知道两种解决方案.
I know at least two solutions.
-
创建 HotSpot可维修性代理工具在内存中查找
MaxJavaStackTraceDepth
变量的地址,然后使用特定于操作系统的机制来更新进程的内存.
Create HotSpot Serviceability Agent tool to find the address of
MaxJavaStackTraceDepth
variable in memory, and then update the memory of the process using OS-specific mechanism.
附加 JVM TI代理拦截StackOverflowErrors
并直接从代理打印堆栈跟踪.
Attach a JVM TI agent that intercepts StackOverflowErrors
and prints a stack trace right from the agent.
这是第一个解决方案的代码(因为它可能更短):
Here is the code for the first solution (as it is presumably shorter):
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写入目标进程的内存.其他操作系统具有不同的界面.
This tool changes the value of MaxJavaStackTraceDepth
in the target process to 1 million.
Note: it uses Linux-specific /proc
API to write into the target process' memory. Other OSes have different interfaces.
如何运行
在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>
这篇关于如何在不重新启动应用程序的情况下获取StackOverflowError的完整stacktrace的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!