天天看點

JStack實作淺析

相信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