?從JDK1.5之后引入了java angent技術
Java Agent 是一種強大的技術,它允許開發者在 JVM 啟動時或運行期間動態地修改類的字節碼,從而實現諸如性能監控、日志記錄、AOP(面向切面編程)等功能
java agent依賴于Instrumentation API,是 Java 提供的一個用于操作已加載類的字節碼的標準API。通過這個API,你可以修改類的字節碼,甚至可以在不重啟應用的情況下重新定義已經加載的類
具體的實現過程是通過一個agent來對字節碼進行修改,agent實際上是一個特殊的jar包,包含了一個或多個特定的方法(premain
或 agentmain
),這些方法會在JVM啟動時或者運行期間被調用
premain:
顧名思義是在應用程序的main函數執行之前執行的
1.在 premain 方法執行時,JVM 已經加載了一些基礎類,但你的應用程序的主要類(以及其它未加載的類)通常還沒有被加載。
2.通過 premain 方法中注冊的 ClassFileTransformer 可以攔截并修改之后加載的所有類的字節碼。
3.如果你希望通過 Java Agent 修改某個類的行為,確保該類在 premain 方法中已經注冊了相應的轉換器是很重要的。對于已經加載的類,你需要使用 retransformClasses 或其他方法來重新處理它們。
對于這種方式實現的agent需要在啟動應用程序的時候通過參數指定執行這個agenr的jar包
agentmain:
需要通過attach API通過 Attach API 動態加載 Agent,允許你對已經加載的類進行重新轉換(retransform)
對于這種方式實現的agent可以在運行時通過Attach API 動態執行
主要注意的時動態執行agent的時候,如果原應用程序中的方法已經在執行的話,正在執行的方法會按照舊的字節碼信息繼續執行,但是agent執行之后的方法執行的是根據agent修改之后新的字節碼信息
新舊共存的原理:
類加載之后方法的字節碼是存在方法區中的,agent修改字節碼之后原來舊的字節碼將會被覆蓋,那么新舊是怎么共存的呢?
實際上jvm中,每加載一個類就會對應生成一個klass對象來表示這個類的元數據結構
方法區中會存儲已被虛擬機加載的類信息、常量、靜態變量等。也就是方法區中的類信息指向這個klass對象
當字節碼被修改之后,方法區中類信息會更新為最新的klass對象,即方法區中存儲的都是最新的類信息
在方法調用執行的時候,會創建一個棧幀,棧幀并不直接指向方法區中的字節碼,而是依賴于類的?Klass
?對象提供的元數據來執行字節碼
所以即使修改了類的字節碼,只是新建了一個klass對象,并且方法區中指向了這個新的klass對象,但是棧幀并不知道方法區中的變化,因為棧幀只是在創建的時候綁定了一個klass對象,并且根據這個klass對象來執行上下文
那兩種agent方法可以共存嗎?
是可以共存的,但是不會在同一 JVM 實例中同時執行。
通過參數啟動的話只會執行premain方法內的邏輯,通過attach API的話只會執行agentmain方法中的邏輯