造成記憶體洩漏的異常處理

祝坤榮發表於2022-04-10

Memory Leak Due To Improper Exception Handling

https://dzone.com/articles/me...
譯:祝坤榮

本文中,我們會討論到我們在生產環境遇到的記憶體問題以及如何解決的。這個應用會在執行幾個小時候後無響應。但並不清楚什麼導致了應用無響應。

技術棧

這個應用執行在AWS雲的規格為r5a.2xlarge的EC2例項。這個應用執行在使用Spring框架的Apache Tomcat伺服器。它也用像S3和Elastic Beanstalk這樣的AWS服務。應用用了個大heap size(-Xmx):48GB。

定位

我們用yCrash工具來定位這個問題。我們讓應用跑15分鐘流量。然後在這個應用上執行yCrash指令碼。yCrash指令碼從應用棧上捕捉了360度資料,分析它們,並展示了問題的根因。yCrash指令碼捕捉的資料包括:Garbage Collection日誌,執行緒dump,heap dump,netstat,vmstat,iostat,top和ps。

yCrash分析材料生成了一份記憶體洩露報告。下面是yCrash生成的heap dump分析報告。

6e75e81d554b7b9b40c35f76bad12f0f.jpeg

圖1:大物件報告

能看到yCrash指出“org.apache.logging.log4j.LogManager”是記憶體中最大的物件。物件佔用了總記憶體的98.2%. 其他物件佔用不到2%的記憶體。以下是這個最大物件的物件樹:

058879a15629387c2ce9ba2261108c2c.jpeg

圖2:物件引用樹

看下物件樹中紅箭頭標的地方。這是應用的起始程式碼。圖2中部分包名被遮蓋了防止能看出具體應用。你能看到這個物件包名為“xxxxxxxx.superpower.Main$1.val$hprofParser”佔用了98.2%的記憶體。
應用有個類叫“xxxxxxxxxxxxxxx.Main.”。很明顯洩露來自這個Main物件。不過,也看不出"xxxxxxxxxxxxxxx.Main$1”是什麼。“$1”指出了這是"xxxxxxxxxxxxxxx.Main”類的第一個匿名內部類。匿名內部類是指你可以在父類中定義一個不用命名的內部類。但這不是一個廣泛使用的Java程式設計實踐。不過匿名內部類不但影響了程式的可讀性也導致定位困難。

以下是“xxxxxxxxxxxxxxx.Main”的高層概要原始碼。為了減少噪音和改進可讀性,類中的無關程式碼都被移除了。

23fc23c3d5cb3f9c7a3d6efa7d68256d.jpeg

圖3:導致記憶體洩露的原始碼

能看到第九行就是匿名內部類。此類繼承了PrintingProgressMeter類。PrintingProgressMeter類繼承了java.util.Thread。無論任何類繼承了java.util.Thread,都會成為一個執行緒。

在第20行,PrintingProgressMeter執行緒是被pm.start()方法啟動的;在21行,呼叫了hprofParser.read()的方法;而在22行,用pm.stopReporting()方法停止了執行緒。這程式碼看著很正常,對嗎?應用的什麼可以觸發一個記憶體洩露呢?

問題:異常處理

在21行hprofParser.read()裡有特定場景可能會拋異常。如果一個異常丟擲,22行的pm.stopReporting()就不會被呼叫。如果這行程式碼不被呼叫,執行緒就會永遠執行不會退出。如果執行緒不退出,執行緒和物件的引用(比如hprofParser)不會被回收。它會導致記憶體洩露。

解決方案

在大多數效能問題裡,定位問題的根因很困難。修復它們很簡單。
這裡就是沒有異常。

8983d3409f3ec13570baafd23484757b.jpeg

圖4:修復記憶體洩露的原始碼

我們將pm.stopReporting()方法移到了finally中。在Java語言中,放在finally程式碼塊中的程式碼無論會不會拋異常都會執行。finally塊的內容可以在這裡https://docs.oracle.com/javas...瞭解下。這樣,即使hprofParser.read()方法拋了異常,pm.stopReporting方法仍會被呼叫,讓執行緒終結。如果執行緒被終結,在垃圾回收時所有物件的引用就會被回收。

當改動後,問題立刻解決了。


本文來自祝坤榮(時序)的微信公眾號「麥芽麵包」,公眾號id「darkjune_think」

開發者/科幻愛好者/硬核主機玩家/業餘翻譯
轉載請註明。

交流Email: zhukunrong@yeah.net

相關文章