Java 以编程方式强制线程转储 - 如“jstack -F -l <​​PID>”

Posted

技术标签:

【中文标题】Java 以编程方式强制线程转储 - 如“jstack -F -l <​​PID>”【英文标题】:Java forced thread dump programmatically - like "jstack -F -l <PID>" 【发布时间】:2015-02-26 14:21:40 【问题描述】:

我正在尝试以编程方式强制执行 Java 线程转储,就像命令 jstack -F -l &lt;PID&gt; 会执行此操作一样。

我的最佳尝试: 我从sun.jvm.hotspot.tools.JStack 创建了一个子类,覆盖run(),其代码类似于sun.jvm.hotspot.tools.JStack.run(),但在最后一行调用start(printstream) 而不是start()

    StackTrace stacktrace = new StackTrace(mixedMode, concurrentLocks);

    try 
        Class<?> stacktraceTool = stacktrace.getClass().getSuperclass();

        Method stacktraceSetAgent = stacktraceTool.getDeclaredMethod("setAgent", new Class<?>[]  BugSpotAgent.class );
        stacktraceSetAgent.setAccessible(true);
        stacktraceSetAgent.invoke(stacktrace, new Object[]  this.getAgent() );

        Method stacktraceSetDebugeeType = stacktraceTool.getDeclaredMethod("setDebugeeType", new Class<?>[]  int.class );
        stacktraceSetDebugeeType.setAccessible(true);
        stacktraceSetDebugeeType.invoke(stacktrace, new Object[]  this.getDebugeeType() );

        stacktrace.run(new PrintStream(this.targetFile));
    
    catch (Exception e) 
        e.printStackTrace(System.err);
    

问题:java.lang.RuntimeException: Attempt to initialize VM twice

所以我做了:

//reset stuff
VM.shutdown();

//clear observer list
Field f = VM.class.getDeclaredField("vmInitializedObservers");
f.setAccessible(true);
VM vmm = VM.getVM();
Object fo = f.get(vmm);
List folist = (List) fo;
folist.clear();

f = sun.jvm.hotspot.runtime.Threads.class.getDeclaredField("threadFactory");
f.setAccessible(true);
f.set(null, null);
f = sun.jvm.hotspot.runtime.Threads.class.getDeclaredField("threadListField");
f.setAccessible(true);
f.setAccessible(true);
f.set(null, null);
f = sun.jvm.hotspot.runtime.Threads.class.getDeclaredField("virtualConstructor");
f.setAccessible(true);
f.set(null, null);
f = sun.jvm.hotspot.runtime.Threads.class.getDeclaredField("access");
f.setAccessible(true);
f.set(null, null);

//reinit observer list
VM.registerVMInitializedObserver(new Observer() 
    public void update(Observable o, Object data) 
        try 
            Method initm = Threads.class.getDeclaredMethod("initialize", new Class<?>[]  TypeDataBase.class );
            initm.invoke(null, new Object[]  VM.getVM().getTypeDataBase() );
        
        catch (Exception e) 
            e.printStackTrace();
        
    
);

但这会导致:

java.lang.RuntimeException: Unable to deduce type of thread from address 0xa5d12400 (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread, or SurrogateLockerThread) at sun.jvm.hotspot.runtime.Threads.createJavaThreadWrapper(Threads.java:152) at sun.jvm.hotspot.runtime.Threads.first(Threads.java:140) at sun.jvm.hotspot.runtime.DeadlockDetector.createThreadTable(DeadlockDetector.java:149) at sun.jvm.hotspot.runtime.DeadlockDetector.print(DeadlockDetector.java:56) at sun.jvm.hotspot.runtime.DeadlockDetector.print(DeadlockDetector.java:39) at sun.jvm.hotspot.tools.StackTrace.run(StackTrace.java:52) at de.chili.savelogfiles.ChiliJStack.run(ChiliJStack.java:40) at sun.jvm.hotspot.tools.Tool.start(Tool.java:221) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at de.chili.savelogfiles.JStackFRunnable.run(JStackFRunnable.java:81) at java.lang.Thread.run(Thread.java:722) Caused by: sun.jvm.hotspot.types.WrongTypeException: No suitable match for type of address 0xa5d12400 (nearest symbol is _ZTV10JavaThread) at sun.jvm.hotspot.runtime.InstanceConstructor.newWrongTypeException(InstanceConstructor.java:62) at sun.jvm.hotspot.runtime.VirtualConstructor.instantiateWrapperFor(VirtualConstructor.java:80) at sun.jvm.hotspot.runtime.Threads.createJavaThreadWrapper(Threads.java:148) ... 13 more sun.jvm.hotspot.utilities.AssertionFailure: Expecting GenCollectedHeap, G1CollectedHeap, or ParallelScavengeHeap, but got sun.jvm.hotspot.gc_interface.CollectedHeap

我没有想法......

【问题讨论】:

【参考方案1】:

我在这里找到了一些有用的东西http://crunchify.com/how-to-generate-java-thread-dump-programmatically

这可能会有所帮助,不知道PID和线程ID是否相同。

 final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
 final ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(<thread Ids>, 100);

【讨论】:

为什么不Thread.getAllStackTraces()? 我不知道 也不知道是否允许。 “如果有安全管理器,则使用 RuntimePermission("getStackTrace") 权限和 RuntimePermission("modifyThreadGroup") 权限调用安全管理器的 checkPermission 方法,查看是否可以获取所有线程的堆栈跟踪。 "

以上是关于Java 以编程方式强制线程转储 - 如“jstack -F -l <​​PID>”的主要内容,如果未能解决你的问题,请参考以下文章

强制用户在 iOS 中以编程方式更新应用程序

如何以编程方式获取 Xcode 显示的线程名称

java面试啥是多线程

以编程方式增加 Spring Boot 中的线程数

以编程方式强制关闭应用程序

如何以编程方式强制 EGORefreshTableHeaderView 更新