JVMTI(Java VM Tool Interface)就是JVM对外暴露的接口。
JVMTI 本质上是在JVM内部的许多事件进行了埋点。通过这些埋点可以给外部提供当前上下文的一些信息。甚至可以接受外部的命令来改变下一步的动作。外部程序一般利用C/C++实现一个JVMTI Agent,在Agent里面注册一些JVM事件的回调。当事件发生时JVMTI调用这些回调方法。Agent可以在回调方法里面实现自己的逻辑。JVMTI Agent是以动态链接库的形式被虚拟机加载的。
JVMTI Agent启动方式: -agentlib:<agent-lib-name>=<options>
JVMTI Agent回调函数:
JVMTI 并不一定在所有的 Java 虚拟机上都有实现,不同的虚拟机的实现也不尽相同。
Java虽然提供了JVMTI,但是对应的Agent需要用C/C++开发,对Java开发者而言并不是非常友好。因此在Java 5的新特性中加入了Instrumentation机制。有了 Instrumentation,开发者可以构建一个基于Java编写的Agent来监控或者操作JVM了,比如替换或者修改某些类的定义等。
开发的Agent需要启动就必须在JVM启动时设置参数,但很多时候我们想要在程序运行时中途插入一个Agent运行。在Java 6的新特性中,就可以通过Attach的方式去加载一个Agent了。
Attach机制的实现涉及到了进程间的通信。主要涉及到两个JVM的线程:
启动Attach Listener方式
Attach Listener线程启动后,就会创建一个监听套接字,并创建了一个文件/tmp/.java_pid的IPC socketFile,之后客户端和目标JVM进程就通过这个socketFile进行通信。客户端可以通过这个socketFile发送相关命令。Attach Listener线程做的事情就是监听这个socketFile,发现有请求就解析,然后根据命令执行不同的方法,最后将结果返回。
JPDA(Java Platform Debugger Architecture)是Java提供的一套用于开发Java调试工具的规范,任何的JDK实现都需要实现这个规范。JPDA是一个Architecture,它包括了三个不同层次的规范。
/ |--------------| / | VM | debuggee - ( |--------------| <------- JVMTI - Java VM Tool Interface / | back-end | / |--------------| / | comm channel - ( | <--------------- JDWP - Java Debug Wire Protocol / | / |--------------| / | front-end | debugger - ( |--------------| <------- JDI - Java Debug Interface / | UI | / |--------------|
JDPA由3个模块组成:
下面启动Debug命令即采用Agent实现:
-agentlib:jdwp=transport=dt_socket,server=y,address=8787
Linux下会使用libjdwp.so这个动态链接库。JVM启动的时候会去调用Agent的Agent_OnLoad方法,这个方法中会去解析我们传进来的transport=dt_socket,server=y,address=8787。
JDK1.5 之前
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8888,onthrow=java.io.IOException,launch=/sbin/echo
JDK1.5 之后,老的还是可以使用
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888,onthrow=java.io.IOException,launch=/sbin/echo
参数说明
样例:
transport=dt_socket,server=y,address=8000 transport=dt_shmem,server=y,suspend=n transport=dt_socket,address=myhost:8000 transport=dt_socket,server=y,address=8000, onthrow=java.io.IOException,launch=/usr/local/bin/debugstub transport=dt_shmem,server=y,onuncaught=y,launch=d:/bin/debugstub.exe
官方参考: optionX
Oracle的JDK提供一些常用工具,用于定位Java问题。
显示当前所有java进程pid的命令,我们可以通过这个命令来查看到底启动了几个java进程。
jps的实现机制:
java程序启动后,会在目录/tmp/hsperfdata_{userName}/下生成几个文件,文件名就是java进程的pid,因此jps列出进程id就是把这个目录下的文件名列一下而已,至于系统参数,则是读取文件中的内容。
用于生成指定进程当前时刻的线程快照,线程快照是当前java虚拟机每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致长时间等待。
主要用于打印指定java进程的共享对象内存映射或堆内存细节。
主要是对java应用程序的资源和性能进行实时的命令行监控,包括了对heap size和垃圾回收状况的监控。
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
样例:
用来查看正在运行的java运用程序的扩展参数,甚至支持在运行时动态地更改部分参数。
jinfo -<option> <pid>
JDK 1.7之后新增,用于向正在运行的JVM发送诊断信息请求。也可用来导出堆,查看java进程,导出线程信息,执行GC等。jcmd拥有jmap的大部分功能,Oracle官方建议使用jcmd代替jmap。
是一个RMI服务器应用程序,用于监控JVM的创建与终止,并提供一个接口允许远程监控工具依附到在本地主机上运行的JVM。
jstatd服务器需要在本地主机上存在一个RMI注册表。jstatd服务器将尝试在默认端口或-p port选项指定的端口附加到该RMI注册表上。如果RMI注册表不存在,jstatd应用程序将会自动创建一个,并绑定到-p port选项指定的端口上,如果省略了-p port选项,则绑定到默认的RMI注册表端口。
注:以上内容收集于互联网多篇文章,在此感谢原作者们。