Java Agent的隔離實現(xiàn)以及卸載時一些坑

這里就涉及到如何回收Perm區(qū)咳榜、或者Metaspace中已經加載的類了酥诽,如果一個類的類加載器對象沒有GC Root關聯(lián),那么可以通過FGC的方式回收這些類琼懊。不過阁簸,如果通過JVM內部的類加載器比如AppClassLoader去加載這些類的話,可能永遠也不能回收了肩碟,所以得通過自定義的類加載器去實現(xiàn)Agent類的加載動作强窖,因為自定義的類加載器對象,我們可以自己控制削祈。

下面是自定義類加載器的實現(xiàn)

publicclassAgentClassLoaderextendsURLClassLoader{? ? publicAgentClassLoader(URL[] urls) {super(urls,ClassLoader.getSystemClassLoader().getParent());? ? }@OverrideprotectedClass loadClass(Stringname, boolean resolve)throwsClassNotFoundException{finalClass loadedClass = findLoadedClass(name);if(loadedClass !=null) {if(resolve) {? ? ? ? ? ? ? ? resolveClass(loadedClass);? ? ? ? ? ? }returnloadedClass;? ? ? ? }// 優(yōu)先從parent(SystemClassLoader)里加載系統(tǒng)類翅溺,避免拋出ClassNotFoundExceptionif(name !=null&& (name.startsWith("sun.") || name.startsWith("java."))) {returnsuper.loadClass(name, resolve);? ? ? ? }// 先從agent中加載try{Class aClass = findClass(name);if(resolve) {? ? ? ? ? ? ? ? resolveClass(aClass);? ? ? ? ? ? }returnaClass;? ? ? ? }catch(Exceptione) {// ignore}returnsuper.loadClass(name, resolve);? ? }}

這樣,通過AgentClassLoader加載的類髓抑,就可以和業(yè)務的類完全隔離開咙崎,在需要回收這些類的時候,只要把AgentClassLoader對象和GC root的關聯(lián)完全掐斷就行吨拍。

不過用了AgentClassLoader之后褪猛,還是遇到了一些坑,比如在Agent中使用Cat的時候羹饰,因為Cat是單例模式伊滋,都是通過?Cat.logEvent?這種方式使用,所以在第一次使用Cat的時候队秩,Cat內部會進行初始化笑旺,比如系統(tǒng)信息上報邏輯。因為業(yè)務邏輯在使用Cat的時候馍资,已經初始化過了一次筒主,在Agent內部使用時,因為是通過AgentClassLoader加載的鸟蟹,又是一個全新的Cat乌妙,相當于那些上報邏輯又初始化了一次,這這種明顯是不行的建钥,那如何在Agent中可以使用業(yè)務加載的那個Cat對象呢藤韵?

后來想到了一個解決方案,通過一個CatAdapt封裝了一下Cat

publicclassCatAdapter {privatestaticfinalLogger logger = LoggerFactory.getLogger(CatAdapter.class);privatestaticMethod logEvent;publicstaticvoidinit(ClassLoader classLoader) {try{ClasscatClazz =Class.forName("com.dianping.cat.Cat",true, classLoader);? ? ? ? ? ? logEvent = catClazz.getMethod("logEvent", String.class, String.class);? ? ? ? }catch(Exception e) {? ? ? ? ? ? logger.error("cat adapter init failed", e);? ? ? ? }? ? }publicstaticvoidlogEvent(String type, String name) {if(logEvent !=null) {try{? ? ? ? ? ? ? ? logEvent.invoke(null, type, name);? ? ? ? ? ? }catch(Exception e) {// ignore}? ? ? ? }? ? }}

在Agent初始化入口的agentmain方法中锦针,獲取當前線程的classLoader

ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();ClasscatAdapter = agentLoader.loadClass("com.**.**.CatAdapter");Method catAdapterInit = catAdapter.getMethod("init", ClassLoader.class);catAdapterInit.invoke(null, currentClassLoader);

又通過agentLoader去加載CatAdapter類荠察,在init方法中置蜀,通過當前線程的classLoader去加載真正的Cat類,這時拿到的Cat的class對象和業(yè)務的Cat class對象是同一個悉盆,從而避免了上述問題盯荤,在Agent內部就可以通過CatAdapter實現(xiàn)Cat方法的代理調用,實現(xiàn)數(shù)據的埋點焕盟。

卸載時的一些坑

為了驗證執(zhí)行FGC時秋秤,是否可以把無用的類回收,遇到了下面這些坑脚翘。

1灼卢、很單純的以為把agentLoader設置為null,我就可以快樂的回收了来农,執(zhí)行了?jmap -histo:live pid?之后鞋真,驚喜的發(fā)現(xiàn),Agent的類還在沃于。

2涩咖、為了看下為什么沒有回收,把堆對象dump下來繁莹,通過mat工具進行分析檩互,找了一個Agent的類,發(fā)現(xiàn)其對象正被agentLoader對象拽著咨演,順騰摸瓜闸昨,發(fā)現(xiàn)agentLoader被線程池的線程拽著,這下明白了薄风,需要把這些線程池給shutdown掉

3饵较、因為在Agent初始化的時候,創(chuàng)建了幾個線程池處理一些內部邏輯遭赂,所以要卸載Agent的時候告抄,這些線程池必須shutdown。

4嵌牺、把線程池shutdown之后,繼續(xù)使用?jmap -histo:live pid?龄糊,發(fā)現(xiàn)這些類特么還在逆粹,真是頑固啊。dump下來炫惩,繼續(xù)分析僻弹,發(fā)現(xiàn)agentLoader還被一個?Finalizer?對象給勾著!這是為啥他嚷,為什么有Finalizer對象勾著它蹋绽?按照我的理解芭毙,只有重寫了finalize方法的類才會有Finalizer對象,一瞬間卸耘,我懷疑是不是線程池的類重寫了finalize方法退敦,一查還真是,在?ThreadPoolExecutor?類中重寫了finalize方法蚣抗。

5侈百、重寫了finalize方法,這種情況理論上要經過兩次GC才會被回收翰铡,執(zhí)行了兩次?jmap -histo:live pid?钝域,Agent的類果然沒了!6А例证!那個開心。

6迷捧、后面又一次不經意的發(fā)現(xiàn)又無法回收了织咧,又只能dump下來,繼續(xù)分析党涕,這次agentLoader對象被業(yè)務線程的threadLocal對象給拽著了烦感,死都不放手。

這一次真的查了好久膛堤,因為不好復現(xiàn)手趣,前前后后驗證了多次,發(fā)現(xiàn)在使用了Agent的Mock功能之后肥荔,就會出現(xiàn)這個問題绿渣,Mock功能會根據業(yè)務配置的String字符串,通過jackson框架反序列化成一個對象并返回燕耿。

jackson在序列化的時候中符,需要開辟一塊內存空間,為了能夠重復利用這塊空間誉帅,jackson默認把這個內存空間封裝成一個SoftReference保存在ThreadLocal中淀散。

這樣每個線程都有一塊內存可以重復使用,這原本是好事蚜锨,但是在我們這档插,變成了一只暗搓搓的手,死死抓著agentLoader不放亚再,導致了所有類都不能回收郭膛。

JsonFactory f =newJsonFactory();f.disable(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING);

最終通過取消這個特性,每次序列化都去創(chuàng)建一塊內存氛悬,這樣就可以避免這個問題则剃,又可以快樂的回收了耘柱。

 在此我向大家推薦一個架構學習交流群。交流學習群號:938837867 暗號:555 里面會分享一些資深架構師錄制的視頻錄像:有Spring棍现,MyBatis调煎,Netty源碼分析,高并發(fā)轴咱、高性能汛蝙、分布式、微服務架構的原理朴肺,JVM性能優(yōu)化窖剑、分布式架構等這些成為架構師必備

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市戈稿,隨后出現(xiàn)的幾起案子西土,更是在濱河造成了極大的恐慌,老刑警劉巖鞍盗,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件需了,死亡現(xiàn)場離奇詭異,居然都是意外死亡般甲,警方通過查閱死者的電腦和手機肋乍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敷存,“玉大人墓造,你說我怎么就攤上這事∶常” “怎么了觅闽?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涮俄。 經常有香客問我蛉拙,道長,這世上最難降的妖魔是什么彻亲? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任孕锄,我火速辦了婚禮,結果婚禮上苞尝,老公的妹妹穿的比我還像新娘硫惕。我一直安慰自己,他們只是感情好野来,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著踪旷,像睡著了一般曼氛。 火紅的嫁衣襯著肌膚如雪豁辉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天舀患,我揣著相機與錄音徽级,去河邊找鬼。 笑死聊浅,一個胖子當著我的面吹牛餐抢,可吹牛的內容都是我干的。 我是一名探鬼主播低匙,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼旷痕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了顽冶?” 一聲冷哼從身側響起欺抗,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎强重,沒想到半個月后绞呈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡间景,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年佃声,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倘要。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡圾亏,死狀恐怖,靈堂內的尸體忽然破棺而出碗誉,到底是詐尸還是另有隱情召嘶,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布哮缺,位于F島的核電站弄跌,受9級特大地震影響,放射性物質發(fā)生泄漏尝苇。R本人自食惡果不足惜铛只,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糠溜。 院中可真熱鬧淳玩,春花似錦、人聲如沸非竿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至承匣,卻和暖如春蓖乘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背韧骗。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工嘉抒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人袍暴。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓些侍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親政模。 傳聞我的和親對象是個殘疾皇子岗宣,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容

  • 很多很多的時候,我都不知道我想要什么览徒。哪怕是現(xiàn)在狈定,我依然不知道我想要什么,雖然我學了會計專業(yè)习蓬,對會計也不抵觸纽什,但,...
    桀玉閱讀 290評論 0 0
  • 離不開使用表格的我們躲叼,天天不停地創(chuàng)建TableView和CloectionView芦缰。在cell上面有switch開...
    OwenKing閱讀 157評論 0 2
  • 今天收獲很多 1、連接枫慷,在任何地方都要用心與周圍人让蕾、物去連接,為溝通做好鋪排 2或听、父母當年盡力了探孝,你自己也可以盡力...
    溫柔青青子吟閱讀 80評論 0 0
  • 在這個物欲橫流急功近利的社會,太多人已患了幸福缺失癥誉裆。人的欲望是無止境的顿颅,它推動著社會不斷發(fā)展,可如果總...
    熹喜閱讀 212評論 0 0