相信Java程式員對jstack都不陌生,今天我們坑進OpenJDK源碼來看看jstack是怎麼實作的。
通過官方文檔的介紹,我們可以知道jstack的源碼在OpenJDK的jdk子項目下,
sun.tools.jstack.JStack
,
/*
* This class is the main class for the JStack utility. It parses its arguments
* and decides if the command should be executed by the SA JStack tool or by
* obtained the thread dump from a target process using the VM attach mechanism
*/
public class JStack { ... }
通過注釋我們可以知道,jstack會根據不同參數決定使用不同的方式來進行thread dump:使用SA的JStack,或者使用attach機制。
通過代碼可以看到,下面三種情況會使用SA的JStack,
- 使用了
選項;-m
- 使用了
選項;-F
- 不是對本地程序而是對遠端server執行jstack;
下面我們來看下使用attach機制實作的thread dump(SA的實作就先欠着啦:)。
關于attach機制,暴露給使用者的其實就是Attach API,其底層實作,簡單來講就是在虛拟機啟動了一個Attach Listener,外部程序通過UNIX socket與其進行通信,發送一些指令讓Attach Listener去執行。具體可以參考官方文檔或者這一篇博文。
下面我們來看下jstack是如何使用attach機制的。
runThreadDump
方法,
// Attach to pid and perform a thread dump
private static void runThreadDump(String pid, String args[]) throws Exception {
VirtualMachine vm = null;
try {
///
// 使用 Attach API
///
vm = VirtualMachine.attach(pid);
} catch (Exception x) {
String msg = x.getMessage();
if (msg != null) {
System.err.println(pid + ": " + msg);
} else {
x.printStackTrace();
}
if ((x instanceof AttachNotSupportedException) &&
(loadSAClass() != null)) {
System.err.println("The -F option can be used when the target " +
"process is not responding");
}
System.exit(1);
}
///
// 發送指令執行thread dump
///
// Cast to HotSpotVirtualMachine as this is implementation specific
// method.
InputStream in = ((HotSpotVirtualMachine)vm).remoteDataDump((Object[])args);
// read to EOF and just print output
byte b[] = new byte[256];
int n;
do {
n = in.read(b);
if (n > 0) {
String s = new String(b, 0, n, "UTF-8");
System.out.print(s);
}
} while (n > 0);
in.close();
vm.detach();
}
可以看到最後是通過
HotSpotVirtualMachine#remoteDataDump
來執行thread dump。來看下這個方法,
// Remote ctrl-break. The output of the ctrl-break actions can
// be read from the input stream.
public InputStream remoteDataDump(Object ... args) throws IOException {
return executeCommand("threaddump", args);
}
private InputStream executeCommand(String cmd, Object ... args) throws IOException {
try {
return execute(cmd, args);
} catch (AgentLoadException x) {
throw new InternalError("Should not get here", x);
}
}
繼續往下跟代碼到Linux平台上的實作,
LinuxVirtualMachine#execute
,可以看到最終通過UNIX socket與Attach Listener進行了通信,發送的就是上面看到的
threaddump
指令。Attach Listener對接收到的指令所執行的操作,在attachListener.cpp,
// names must be of length <= AttachOperation::name_length_max
static AttachOperationFunctionInfo funcs[] = {
{ "agentProperties", get_agent_properties },
{ "datadump", data_dump },
{ "dumpheap", dump_heap },
{ "load", JvmtiExport::load_agent_library },
{ "properties", get_system_properties },
{ "threaddump", thread_dump },
{ "inspectheap", heap_inspection },
{ "setflag", set_flag },
{ "printflag", print_flag },
{ "jcmd", jcmd },
{ NULL, NULL }
};
看下
threaddump
所對應的
thread_dump
方法,
// Implementation of "threaddump" command - essentially a remote ctrl-break
// See also: ThreadDumpDCmd class
//
static jint thread_dump(AttachOperation* op, outputStream* out) {
bool print_concurrent_locks = false;
if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) {
print_concurrent_locks = true;
}
// thread stacks
VM_PrintThreads op1(out, print_concurrent_locks);
VMThread::execute(&op1);
// JNI global handles
VM_PrintJNI op2(out);
VMThread::execute(&op2);
// Deadlock detection
VM_FindDeadlocks op3(out);
VMThread::execute(&op3);
return JNI_OK;
}
是以再往下就得坑進HotSpot的源碼,
VMThread::execute
,才能看到最終HotSpot是怎麼執行的thread dump了。這裡就不繼續挖坑了,感興趣的同學可以繼續跟下去 ^_^
嗯,今天就先到這,have fun la