本文介绍JDK 8的jps、jstat、jstack … 的工作原理。
Jvmstat Performance Counters
jps和jstat命令使用的是Jvmstat Performance Counters。
jps
先说结论:
- jps命令扫描的是
$TMPDIR/hsperfdata_$usr
下的PID文件。比如在 Linux 系统下,/tmp/hsperfdata_foo/2121
,这体现两个信息,JVM进程的PID是2121,启动这个进程的是foo
用户。
源码脉络:
- Jps.java
- MonitoredHost (javadoc) 有三子类。
- 看local实现:MonitoredHostProvider.java
- LocalVmManager.java
- PerfDataFile.java 规定了PID文件的匹配模式,tmpDirName 属性是平台相关的
- VMSupport.c -> jvm.h -> jvm.cpp 规定了
JVM_GetTemporaryDirectory
的返回值
所以说,如果你把别的机器的/tmp/hsperfdata_<usr>/<pid>
复制到你本地,jps也是能够返回结果的。
但是,前提是PID得在你的机器上存在,比如别的机器上PID是2121,那么你的机器上也必须有PID=2121的进程,无论这个进程是什么。否则jps会返回 -- process information unavailable
这样的信息。
如果你这样执行jps(其实所有命令都可以这样执行),可以看到异常:
|
|
顺着这个错误分析PerfDataBuffer.java,可以发现解决的办法就是创建一个/tmp/hsperfdata_<pid>
的文件。
jstat
先说结论:
- jstat和jps一样,读取的是
$TMPDIR/hsperfdata_$usr
下的PID文件,这个文件准确来说应该是 perfdata。这里面有 jstat 所想要的一切。 - JVM在运行过程中会更新 perfdata,perfdata 采用的是mmap机制(内存映射文件),详见JVM源码分析之jstat工具原理完全解读
- 顺带一提,mmap文件内容的修改是无法通过 inotify(7) 探测到的(见Limitations and caveats)。
源码脉络:
- Jstat.java,你可以看到两个隐藏参数
-list
和-snap
,这不是我们的主题,关键看logSamples()方法 - 可以看到同样依赖于 MonitoredHost (javadoc)
- 然后得到 MonitoredVm,类层级结构是 BufferedMonitoredVm -> AbstractMonitoredVm -> LocalMonitoredVm
- LocalMonitoredVm 用到了 PerfDataBuffer
Dynamic Attach Mechanism
jstack和jmap命令使用的是Dynamic Attach Mechanism。
jstack
先说结论:
- 和socket文件
/tmp/.java_pid$pid
通信 - 如果这个文件不存在,则
touch /proc/$pid/cwd/.attach_pid$pid
文件 - 然后
kill -3 $pid
,JVM就会创建socket文件 - 然后把
attach_pid$pid
文件可以删掉
源码脉络:
- Jstack.java 使用 VirtualMachine.attach()
- VirtualMachine,有一个子类HotSpotVirtualMachine
- VirtualMachine.attach() 依赖 AttachProvider javadoc
- AttachProvider 子类 HotSpotAttachProvider 子类 LinuxAttachProvider
- 然后用到了 LinuxVirtualMachine,这里描述了上述逻辑。
jmap
jmap的机制和jstack一摸一样,不做赘述。
评论