Java探針技術-Instrumentation與ClassFileTransformer--字節碼轉換工具


  一個代理實現ClassFileTransformer接口用於改變運行時的字節碼(class File),這個改變發生在jvm加載這個類之前。對所有的類加載器有效。

  class File這個術語定義於虛擬機規范3.1,指的是字節碼的byte數組,而不是文件系統中的class文件。

接口中只有一個方法:

byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte[] 
classfileBuffer)throws IllegalClassFormatException;


ClassFileTransformer需要添加到Instrumentation實例中才能生效。

  獲取Instrumentation實例的方法有2種:

  虛擬機啟動時,通過agent class的premain方法獲得
  虛擬機啟動后,通過agent class的agentmain方法獲得

  一旦agent參數獲取到一個instrumentation,agent將會在任意時候調用實例中的方法。

  agent應該以jar包的形式存在,也就是說agent所在的類需要單獨打包一個jar包,jar包的manifest文件指定agent class。文件中包含Premain-Class屬性,agent class類必須實現public static premain 方法,實際應用的main方法在這個方法之后執行。

  premain 方法有2種簽名,虛擬機優先調用

public static void premain(String agentArgs, Instrumentation inst);

如果沒有上一種,則調用下一種

public static void premain(String agentArgs); 

  通過這個 -javaagent:jarpath[=options] 參數,啟動實際應用,就會自帶agent。如果agent啟動失敗,jvm會終止。

  在虛擬機啟動后,啟動agent需要滿足以下條件

  agent所在 的jar包的manifest文件中必須包含Agent-Class屬性,值為agent class。

  agent類必須有public static agentmain方法。

  系統類加載器必須支持添加一個agent的jar包到系統類路徑system class path

  這個方法也有2種簽名,優先加載第一種,第一種沒有,就加載第二種。

 

public static void agentmain(String agentArgs, Instrumentation inst);

public static void agentmain(String agentArgs);


  如果agent是在jvm啟動后啟動,那么premain就不會執行了。也就是說一個agent的2種方法只會啟動一種。premain和agentmain是二選一的。agentmain拋出異常,不會導致jvm終止。

  第二種啟動方式,先用jps獲取進程id,然后啟動agentjar包。

VirtualMachine 在jdk的lib下面的tools.jar中,如果不在classpath的話,要加進去。

VirtualMachine vm = VirtualMachine.attach("3134");
try { 
  vm.loadAgent("/../agent.jar"); 
 } finally { 
  vm.detach(); 
}

agent的jar包中manifest中可以有的屬性:

Premain-Class 指定代理類
Agent-Class 指定代理類
Boot-Class-Path 指定bootstrap類加載器的搜索路徑,在平台指定的查找路徑失敗的時候生效, 可選
Can-Redefine-Classes 是否需要重新定義所有類,默認為false,可選。
Can-Retransform-Classes 是否需要retransform,默認為false,可選。

  有兩種ClassFileTransformer,根據canRetransform決定是哪一種。
  在向Instrumentation#addTransformer添加轉換器的時候,會指定canRetransform,默認為false。決定retransformation是否可用。

  一旦一個transformer被注冊到instrumentation中,每當一個類被定義(ClassLoader.defineClass)或被重新定義(Instrumentation.redefineClasses)時,它都會被調用。

  如果retransformation可用,那么一個類被retransformation(Instrumentation.retransformClasses)時,transformer也會被調用。

  存在多個transformers時,每個transformer會進行鏈式調用。

多個transformers調用順序:

  • Retransformation不可用的
  • Retransformation不可用的native 的transformation
  • Retransformation可用的
  • Retransformation可用的native 的transformation
  • 發生retransformations的時候,Retransformation不可用的transformers不會被調用。
  • 同一種transformers按照注冊順序執行。
  • native的transformers通過ClassFileLoadHook提供。
  •   如果一個transformer不想改變任何代碼,那么返回null。否則,應該創建一個新的byte[],不能修改classfileBuffer。一個transformer拋出異常,后續的transformer依然會執行,拋異常和返回Null效果相同。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM