想必很多人據說過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