天天看点

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