当我向我的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时得到的一切;不只是堆栈跟踪。
是的你可以。我一直在成功使用此代码来调试随机触发的并发错误:
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正在做的事情,除了在同一个进程中。
不是一个完整的解决方案,但它应该给你方向:
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;
}
我使用此代码获取带有线程转储信息的字符串
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();
}