日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

寻找内存泄漏:一个案例研究

發(fā)布時(shí)間:2023/12/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 寻找内存泄漏:一个案例研究 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一周前,我被要求修復(fù)一個(gè)有內(nèi)存泄漏問題的webapp。 考慮到過去兩年左右的時(shí)間里我已經(jīng)看到并修復(fù)了數(shù)百個(gè)泄漏,我想這有多難。

但是事實(shí)證明這是一個(gè)挑戰(zhàn)。 12小時(shí)后,我發(fā)現(xiàn)該應(yīng)用程序中不少于5個(gè)漏洞,并設(shè)法修復(fù)了其中4個(gè)漏洞。 我認(rèn)為這將是值得分享的經(jīng)歷。 對(duì)于那些不耐煩的人–總而言之,我發(fā)現(xiàn)了

  • MySQL驅(qū)動(dòng)程序啟動(dòng)后臺(tái)線程
  • 重新部署時(shí)未卸載java.sql.DriverManager
  • BoneCP從錯(cuò)誤的類加載器加載資源
  • 數(shù)據(jù)源已注冊(cè)到JNDI樹中,阻止了卸載
  • 使用終結(jié)器的連接池與在單獨(dú)線程中運(yùn)行的Google的參考隊(duì)列實(shí)現(xiàn)相關(guān)聯(lián)

當(dāng)前的應(yīng)用程序是一個(gè)簡(jiǎn)單的Java Web應(yīng)用程序,具有一些連接到關(guān)系數(shù)據(jù)庫(kù)的數(shù)據(jù)源,中間是Spring以將內(nèi)容粘合在一起,并將簡(jiǎn)單的JSP頁(yè)面呈現(xiàn)給最終用戶。 沒有魔術(shù)。 還是我想。 男孩,我錯(cuò)了。

第一站 -MySQL驅(qū)動(dòng)程序。 顯然,最常見的MySQL驅(qū)動(dòng)程序會(huì)在后臺(tái)啟動(dòng)線程,以清理未使用和未關(guān)閉的連接。 到目前為止,一切都很好。 但是要注意的是,這個(gè)新創(chuàng)建的線程的上下文類加載器是您的Web應(yīng)用程序類加載器。 這意味著,在運(yùn)行此線程并嘗試取消部署webapp的同時(shí),其類加載器被甩在了后面–加載了所有類。

顯然,從2012年7月到2013年2月,發(fā)現(xiàn)此錯(cuò)誤后,對(duì)其進(jìn)行了修復(fù)。 您可以按照MySQL問題跟蹤器中的討論進(jìn)行操作。 最終實(shí)現(xiàn)的解決方案是API的shutdown()方法,開發(fā)人員在重新部署之前應(yīng)該知道要調(diào)用該方法。 好吧,我沒有。 我敢打賭,你們當(dāng)中有99%的人也沒有。

在典型的Java Web應(yīng)用程序中,有一個(gè)適合此類關(guān)閉掛鉤的好地方,即ServletContextListener類contextDestroyed()方法。 每次銷毀servlet上下文時(shí),都會(huì)調(diào)用此特定方法,例如,這種情況通常發(fā)生在重新部署期間。 可能有相當(dāng)多的開發(fā)人員意識(shí)到這個(gè)地方的存在,但是實(shí)際上有多少人意識(shí)到需要清理這個(gè)特定的鉤子呢?

回到該應(yīng)用程序,該應(yīng)用程序還沒有被修復(fù)。 我的第二個(gè)發(fā)現(xiàn)還與上下文類加載器和數(shù)據(jù)源有關(guān)。 使用com.jdbc.myslq.Driver時(shí),它將自身注冊(cè)為java.sql.DriverManager類中的驅(qū)動(dòng)程序。 同樣,這是有良好意圖的。 畢竟,這是您的應(yīng)用程序用來確定在連接到數(shù)據(jù)庫(kù)URL時(shí)如何為每個(gè)查詢選擇正確的驅(qū)動(dòng)程序的方法。 但您可能會(huì)猜到一個(gè)問題:該DriverManager是在引導(dǎo)類加載器中加載的,而不是在Web應(yīng)用程序的類加載器中加載的,因此在重新部署應(yīng)用程序時(shí)無法將其卸載。

現(xiàn)在使事情真正變得奇怪的是,沒有一般的方法可以自行注銷驅(qū)動(dòng)程序。 對(duì)您嘗試注銷的類的引用似乎是故意向您隱藏的。 在這種特殊情況下,我很幸運(yùn),應(yīng)用程序中使用的連接池能夠注銷驅(qū)動(dòng)程序。 萬(wàn)一我記得問。 回顧過去的類似案例,這是我第一次看到在連接池中實(shí)現(xiàn)這種功能。 在此之前,我曾經(jīng)必須枚舉在DriverManager中注冊(cè)的所有JDBC驅(qū)動(dòng)程序,以找出應(yīng)該注銷的驅(qū)動(dòng)程序。 我無法向任何人推薦這種體驗(yàn)。

我想應(yīng)該是這樣。 同一應(yīng)用程序中的兩次泄漏已經(jīng)可以忍受一個(gè)以上。 錯(cuò)誤。 泄漏報(bào)告中盯著我的第三個(gè)問題是sun.awt.AppContext及其靜態(tài)字段mainAppContext。 什么? 我不知道該類應(yīng)該做什么,但是我很確定手頭的應(yīng)用程序沒有以任何方式使用AWT 。 因此,我啟動(dòng)了一個(gè)調(diào)試器,以找出是誰(shuí)加載了此類(以及為什么)。 另一個(gè)驚喜:它是com.sun.jmx.trace.Trace.out()。 您能想到com.sun.jmx類將之稱為sun.awt類的充分理由嗎? 我當(dāng)然不能。 但是,該類堆棧源自連接池BoneCP 。 跳過導(dǎo)致該特定內(nèi)存泄漏的代碼行的絕對(duì)零方式。 解? 我的ServletContextListener.contextInitialized()中的以下魔咒:

Thread.currentThread().setContextClassLoader(null); // Force the AppContext singleton to be created and initialized without holding reference to WebAppClassLoder sun.awt.AppContext.getAppContext();

但是我仍然沒有做完:有些東西還在泄漏。 在這種情況下,我發(fā)現(xiàn)我們的應(yīng)用程序?qū)⒋藬?shù)據(jù)源綁定到InitialContext() JNDI樹,這是一種很好的,標(biāo)準(zhǔn)化的方法,用于綁定對(duì)象以供將來發(fā)現(xiàn)。 但是,再次強(qiáng)調(diào)–使用這種好東西時(shí),您必須使用完全相同的contextDestroy()方法從JNDI樹中解除綁定此數(shù)據(jù)源來清理自己。

好吧,到目前為止,我們遇到了一些邏輯問題,盡管很少見并且有些晦澀難懂的問題,但是有了一些推理和google-fu很快就解決了。 我的第五個(gè)也是最后一個(gè)問題就是這樣。 我仍然因?yàn)镺utOfMemoryError:PermGen而使應(yīng)用程序崩潰。 Plumbr和Eclipse MAT都向我報(bào)告說,罪魁禍?zhǔn)资前盐业念惣虞d器扣為人質(zhì)的一個(gè)線程,名為com.google.common.base.internal.Finalizer。 “這家伙到底是誰(shuí)?” –是黑暗吞沒我之前我的最后一個(gè)想法。 幾個(gè)小時(shí)和四杯咖啡之后,我發(fā)現(xiàn)自己盯著三行:

emf.close(); emf = null; ds = null;

很難準(zhǔn)確回憶一下在這段時(shí)間里發(fā)生的事情。 我對(duì)WeakReferences , ReferenceQueues , Finalizers , Reflection擁有遠(yuǎn)程記憶,而我第一次在野外看到PhantomReference 。 即使到了今天,我仍然無法完全解釋為什么連接池使用終結(jié)器以及將終結(jié)器綁定到在單獨(dú)線程中運(yùn)行的Google的參考隊(duì)列實(shí)現(xiàn)的目的以及目的。

我也無法解釋為什么關(guān)閉javax.persistence.EntityManagerFactory (在上面的代碼中命名為emf并保存在應(yīng)用程序自己的類之一中的靜態(tài)引用中)的原因; 因此,我不得不手動(dòng)使該引用無效。 以及對(duì)該工廠使用的數(shù)據(jù)源的類似靜態(tài)引用。 我確信Java的GC可以整天處理循環(huán)引用,但是,即使對(duì)于他來說,類,靜態(tài)引用,對(duì)象,終結(jié)器和引用隊(duì)列的神奇組合似乎也很難。 因此,這是我漫長(zhǎng)的職業(yè)生涯中的第一次,我不得不取消Java參考。

我是一個(gè)謙虛的人,因此不能說我在短短12個(gè)小時(shí)內(nèi)能最有效地找到以上所有方法。 但是我必須承認(rèn),過去三年來我?guī)缀跻恢痹谔幚韮?nèi)存泄漏。 而且我什至擁有自己的創(chuàng)造力Plumbr來幫助我(實(shí)際上,其中五分之四的泄漏是Plumbr在30分鐘左右的時(shí)間內(nèi)發(fā)現(xiàn)的)。 但是要真正解決這些泄漏,我花了一個(gè)多工作日的時(shí)間。

總體而言-在Java EE和/或類加載器世界中,顯然有些問題。 開發(fā)人員必須記住所有這些掛鉤和配置技巧,這是不正常的,因?yàn)檫@根本不可能。 畢竟,我們喜歡用頭腦去做一些富有成效的事情。 而且,從與兩個(gè)流行的servlet容器( Tomcat和Jetty )捆綁在一起的變通辦法可以看出,問題很嚴(yán)重。 但是,解決該問題不僅需要緩解某些癥狀,還需要解決潛在的設(shè)計(jì)錯(cuò)誤。

參考: 尋找 內(nèi)存泄漏:來自我們的JCG合作伙伴 Nikita Salnikov Tarnovski (來自Plumbr博客) 的案例研究 。

翻譯自: https://www.javacodegeeks.com/2013/03/hunting-down-memory-leaks-a-case-study.html

總結(jié)

以上是生活随笔為你收集整理的寻找内存泄漏:一个案例研究的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。