想必很多人据说过javaagent,可是不多人据说Instrumentation,其实Instrumentation就是javaagent的实现机制,说到Instrumentation,就必须想了解java的attach机制,那就先说下attach的实现。 你们进行jstack的时候,是否是常常看到两个线程
Signal Dispatcher
和Attach Listener
线程,可能不知道是干吗的吧,这两个线程是实现attach的关键所在,其中前者是在jvm启动的时候就会建立的
,后者只有接收过attach请求的时候vm才会建立
,顾名思义,Signal Dispatcher是分发信号的
, Attach Listener 是处理attach请求的
,那么二者有什么关系呢,当咱们执行attach方法的时候,会向目标vm发出一个SIGQUIT 的信号,目标vm收到这个信号以后就会建立Attach Listener线程了
,固然jvm保证了不会多建立。java
Attach机制说得简单点就是提供A进程能够连上B进程(固然是java进程),建立socket进行通讯,A经过发命令给B,B而后对命令进行截取从本身的vm中获取信息发回给客户端vm,可是并非随便发指令都会处理的,那么attach Listener接收哪些命令呢,以下所示:linux
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 } };
Instrumentation的实现其实主要使用了load
这个指令,它用来实现让target vm动态加载agentlib
,Instrumentation的实如今一个名为libinstrument.dylib
的动态lib库,linux下是libinstrument.so
,它是基于JVMTI
接口实现的,所以在对其进行load的时候会建立一个agent实例,并往JVMTI环境注册一些回调方法,好比监听类文件加载的事件
,vm初始化完成事件
等,执行Agent_OnAttach,这里会建立一个Instrumentation实例并返回给用户供你们扩展Instrumentation,好比增长一些transform,并会执行Instrumentation实例的 loadClassAndCallAgentmain
方法,该方法主要执行agent的MF文件里定义的 Agent-Class类的agentmain方法,当vm初始化完毕以后,会调用loadClassAndCallPremain
方法,该方法主要执行agent的MF文件里定义的Agent-Class类的premain方法。在类进行加载的时候会调用Instrumentation的transform方法
,能够看看参数里有个byte数组,这个数组其实就是正在加载的class字节码,因此若是要字节码加强在这里就能够入手啦,甚至能够实现偷天换日。api
若是在vm启动过程当中加载agent,那么会在vm初始化过程当中先执行libinstrument.dylib里InvocationAdapter.c的Agent_OnLoad方法
,该方法主要:实例化agent,解析agent的MF文件,将相关属性取出来,并注册JVMTI的一些回调函数,在vm初始化完成以后,会经过回调函数去实例化Instrumentation实现对象,设置ClassFileLoadHook函数,并调用Pre-Main指定类的premain方法。数组
若是在运行期经过attach api来load agent,那么会在收到load指令以后,会调用InvocationAdapter.c的Agent_OnAttach方法
,其实现基本和Agent_OnLoad一致,只是会调用Agent-Class的agentmain方法
,还有点不一样就是对vm init事件没有再关注(都运行期了,关注也没用),而是直接对ClassFileLoad关注,也不会再调用Pre-Main指定的类的premain方法(顾名思义,是在执行main方法以前执行的,因此运行期搞执行Pre-Main的class也不妥)。jvm