占小狼,转载请注明原创出处,谢谢!
jmap、jstack等工具可以访问虚拟机中的堆对象、线程信息等,可以通过两种方式实现
1、attach方式
2、SA方式
attach方式
这种方式,在之前的文章已经分析过,底层通过socket进行通信,jmap进程好比一个客户端,运行的虚拟机看成服务端,有一个叫"Attach Listener"的线程,专门负责监听attach的请求,并在虚拟机中执行对应的代码。
更多详情点击 jmap命令的实现原理解析
SA方式
首先,我们先看看SA可以做什么?
1、从运行的Java进程中读取数据
2、从数据中,解析出所有Hotspot数据结构
3、从Hotspot数据结构中国,解析出所有的Java对象
这里需要清楚的是:
SA是运行在单独的进程中,和目标Java进程是隔离的,而且在使用SA工具时,不会在目标Java进程中执行任何代码,而是读取目标Java进程中的数据,然后在自身进程中处理,在SA读取数据时,目标Java进程会被挂起。
那么,SA是如何读取到目标Java进程的呢?不同的系统,有不同的方式:
1、Solaris系统中,通过 libproc 实现
2、Linux系统中,通过 /proc和ptrace 实现
3、Windows系统中,通过 dbgeng.dll library 实现
下面以Linux为例,看看是如何一步一步实现的。
假设执行"jmap -heap <pid>",该命令对应的实现类
"sun.jvm.hotspot.tools.HeapSummary.java"
SA的工具类都继承了Tool类,通过start方法启动,start方法中会初始化一个BugSpotAgent,并通过BugSpotAgent的attach方法与目标Java进程建立联系,attach方法实现如下:
在setupDebugger方法中,根据不同平台初始化debugger,attach动作最终由具体的平台相关的JVMDebugger对象完成,在Linux平台,使用LinuxDebuggerLocal对象,LinuxDebuggerLocal类中具体的attach实现如下:
最终调用了一个本地方法
private native void attach0(int pid) throws DebuggerException;
本地方法attach0的实现位于LinuxDebuggerLocal.c
中
在本地方法attach0中,看到有一个Pgrab方法,跟进去...
Pgrab方法的注释也说明该方法可以attach到目标进程上,从ptrace_attach方法再跟进去...
这里,我们看到了ptrace命令
ptrace(PTRACE_ATTACH, pid, NULL, NULL)
其中PTRACE_ATTACH,可以实现attach到一个指定pid的进程。
SA成功attach到目标Java进程之后,执行setupVM,初始化Hotspot数据结构,之后的数据获取通过/proc实现,这里不再深入分析了。