【问题标题】:Trigger complete stack dump programmatically?以编程方式触发完整的堆栈转储?
【发布时间】:2012-10-09 13:42:51
【问题描述】:

当我向我的 java 进程发送一个 SIGQUIT 命令(使用 kill -3 或 kill -QUIT )时,它会将所有堆栈的跟踪打印到 stderr,其中包含有关持有的锁和死锁检测的信息。我可以从程序内部以某种方式触发它吗?我想在每次某个操作花费太长时间时自动执行此操作。

我知道可以获得堆栈跟踪(请参阅Is there a way to dump a stack trace without throwing an exception in java?Thread dump programmatically /JDI (Java Debugger Interface))。但我想查看全部内容:堆栈跟踪、线程状态、持有的锁、阻塞的锁、死锁检测等,即我发送 SIGQUIT 时得到的所有内容;不仅仅是堆栈跟踪。

【问题讨论】:

  • 这不是重复的。其他问题只与堆栈跟踪有关。我想要所有的东西,堆栈跟踪,线程状态,持有的锁,阻塞的锁,死锁检测等,即我发送 SIGQUIT 时得到的所有东西。我找到的最接近的答案是使用 ThreadMXBean,但这不是一个完整的解决方案,无论哪种方式,现在我都无法将它添加到这个问题中。

标签: java jvm signals stack-trace


【解决方案1】:

是的,你可以。我一直在成功地使用此代码调试随机触发的并发错误:

package utils.stack;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.function.Supplier;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

public interface DiagnosticCommand {
    String threadPrint(String... args);

    DiagnosticCommand local = ((Supplier<DiagnosticCommand>) () -> {
        try {
            MBeanServer server = ManagementFactory.getPlatformMBeanServer();
            ObjectName name = new ObjectName("com.sun.management", 
                "type", "DiagnosticCommand");
            return JMX.newMBeanProxy(server, name, DiagnosticCommand.class);
        } catch(MalformedObjectNameException e) {
            throw new AssertionError(e);
        }
    }).get();

    static void dump() {
        String print = local.threadPrint();
        Path path = Paths.get(LocalDateTime.now() + ".dump.txt");
        try {
            byte[] bytes = print.getBytes("ASCII");
            Files.write(path, bytes);
        } catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}

它需要 Java 8 和 HotSpot 作为 JVM,因为它模仿 jstack 正在做的事情,除了在同一个进程中。

【讨论】:

    【解决方案2】:

    不是一个完整的解决方案,但它应该为您指明方向:

    public StringBuffer getInfoXmlString() {
    StringBuffer serverInfo = new StringBuffer(3000);
    
    serverInfo.append("<ServerInfo>");
    ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
    ThreadGroup parent;
    while ((parent = rootGroup.getParent()) != null) {
      rootGroup = parent;
    }
    
    serverInfo.append("<Threads>\r\n");
    serverInfo.append(listThreads(rootGroup, "  "));
    serverInfo.append("</Threads>\r\n");
    
    serverInfo.append("</ServerInfo>\r\n");
    }
    
    private StringBuffer listThreads(ThreadGroup group, String indent) {
    StringBuffer threadInfo = new StringBuffer(5000);
    threadInfo.append(indent);
    threadInfo.append("<ThreadGroup name=\"");
    threadInfo.append(group.getName());
    threadInfo.append("\" class=\"");
    threadInfo.append(group.getClass().getName());
    threadInfo.append("\">\r\n");
    
    int nt = group.activeCount();
    Thread[] threads = new Thread[nt * 2 + 10];
    nt = group.enumerate(threads, false);
    
    // List every thread in the group
    for (int i = 0; i < nt; i++) {
      Thread t = threads[i];
      threadInfo.append(indent);
      threadInfo.append("  <Thread name=\"");
      threadInfo.append(t.getName());
      threadInfo.append("\" class=\"");
      threadInfo.append(t.getClass());
      threadInfo.append("\" alive=\"");
      threadInfo.append(t.isAlive());
      threadInfo.append("\"/>\r\n");
    }
    // Recursively list all subgroups
    int ng = group.activeGroupCount();
    ThreadGroup[] groups = new ThreadGroup[ng * 2 + 10];
    ng = group.enumerate(groups, false);
    for (int i = 0; i < ng; i++) {
      threadInfo.append(listThreads(groups[i], indent + "  "));
    }
    threadInfo.append(indent);
    threadInfo.append("</ThreadGroup>\r\n");
    
    return threadInfo;
    }
    

    【讨论】:

      【解决方案3】:

      我使用此代码获取带有胎面转储信息的字符串

          public static String generateThreadDump() {
              final StringBuilder dump = new StringBuilder();
              final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
              final ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
              for (ThreadInfo threadInfo : threadInfos) {
                  dump.append('"');
                  dump.append(threadInfo.getThreadName());
                  dump.append("\" ");
                  final Thread.State state = threadInfo.getThreadState();
                  dump.append("\n   java.lang.Thread.State: ");
                  dump.append(state);
                  final StackTraceElement[] stackTraceElements = threadInfo.getStackTrace();
                  for (final StackTraceElement stackTraceElement : stackTraceElements) {
                      dump.append("\n        at ");
                      dump.append(stackTraceElement);
                  }
                  dump.append("\n\n");
              }
              return dump.toString();
          }
      

      【讨论】:

        【解决方案4】:

        我找到了这个选项

        ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
        
        for (ThreadInfo ti : threadMxBean.dumpAllThreads(true, true)) {
            System.out.print(ti.toString());
        }
        

        【讨论】:

          猜你喜欢
          • 2010-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-23
          • 2020-07-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多