java instrument 初探

java在1.5引入java.lang.instrument,你可以由此實現一個java agent,通過此agent來修改類的字節碼即改變一個類。本文中,會通過java instrument 實現一個簡單的profiler。當然instrument並不限於profiler,instrument可以做很多事情,

它類似一種更低級,更鬆耦合的AOP,可以從底層來改變一個類的行爲,你可以由此產生無限的遐想。

接下來要做的事情,就是計算一個方法所花的時間,通常我們會在代碼這麼寫:
在方法開始開頭加入long stime = System.nanoTime();
在方法結尾通過System.nanoTime()-stime得出方法所花時間,你不得不在你想監控的每個方法中寫入重複的代碼,
好一點的情況,你可以用AOP來幹這事,但總是感覺有點彆扭,這種profiler的代碼還是打包在你的項目中,

java instrument使得這更乾淨。

 

1) 寫agent類

 

2)寫ClassFileTransformer類
 

上面兩個類就是agent的核心了,jvm啓動時並會在應用加載前會調用 PerfMonAgent.premain, 然後PerfMonAgent.premain中實例化了一個定製的ClassFileTransforme即 PerfMonXformer
並通過inst.addTransformer(trans);把PerfMonXformer的實例加入Instrumentation實例(由jvm傳入),這就使得應用中的類加載的時候, PerfMonXformer.transform都會被調用,你在此方法中
可以改變加載的類,真的有點神奇,爲了改變類的字節碼,我使用了jboss的javassist,雖然你不一定要這麼用,但jboss的javassist真的很強大,讓你很容易的改變類的字節碼。在上面的方法中
我通過改變類的字節碼,在每個類的方法入口中加入了long stime = System.nanoTime();,在方法的出口加入了System.out.println("methodClassName.methodName:"+(System.nanoTime()-stime));

 

3) 打包agent
對於agent的打包,有點講究,
3.1)
jar的META-INF/MANIFEST.MF加入Premain-Class: xx, xx在此語境中就是我們的agent類,即org.toy.PerfMonAgent
3.2)
如果你的agent類引入別的包,需使用Boot-Class-Path: xx,xx在此語境中就是上面提到的jboss javassit 即 /home/pwlazy/.m2/repository/javassist/javassist/3.8.0 .GA/javassist-3.8.0.GA.jar

下面附上maven 的pom


最終打成一個包toy-inst-1.0-SNAPSHOT.jar


4)打包應用
隨便寫個應用

  

最終打成一個包toy-1.0-SNAPSHOT.jar

5)執行命令



java選項中有-javaagent:xx,xx就是你的agent jar,java通過此選項加載agent,由agent來監控classpath下的應用。

最後的執行結果:

 

我們由執行結果可以看出執行順序以及通過改變org.toy.App的字節碼加入監控代碼確實生效了。

你也可以發現通過instrment實現agent是的監控代碼和應用代碼完全隔離了。

發佈了200 篇原創文章 · 獲贊 7 · 訪問量 106萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章