[英]Memory leak in a Java web application
我有一個在Tomcat 7上運行的Java Web應用程序似乎有內存泄漏。 應用程序的平均內存使用量在負載下隨着時間線性增加(使用JConsole確定)。 在內存使用率達到穩定水平后,性能會顯着下降。 響應時間從~100ms到[300ms,2500ms],因此這實際上導致了實際問題。
我的應用程序的JConsole內存配置文件:
使用VisualVM,我看到至少有一半的內存被字符數組(即char [])使用,並且字符串的大多數(大致相同數量的每個,300,000個實例)是以下之一:“分配失敗” ,“復制”,“次要GC的結束”,所有這些似乎都與垃圾收集通知有關。 據我所知,應用程序根本不監視垃圾收集器。 VisualVM找不到任何這些字符串的GC根,所以我很難跟蹤它。
內存分析器堆轉儲:
我無法解釋為什么內存使用量會如此高,但我有一個關於為什么性能一旦降低就會降低的理論。 如果內存碎片化,應用程序可能需要很長時間才能分配連續的內存塊來處理新請求。
將其與內置的Tomcat服務器狀態應用程序進行比較,內存會增加並保持穩定,但不會像我的應用程序那樣達到很高的“底限”。 它也沒有大量無法訪問的char []。
Tomcat服務器狀態應用程序的JConsole內存配置文件:
Tomcat服務器狀態applicationp的內存分析器堆轉儲:
這些字符串可以在哪里分配,為什么它們不被垃圾收集? 是否存在可能影響此問題的Tomcat或Java設置? 是否有特定的包可能會影響這個?
我從tomcat\\bin\\setenv.bat
刪除了以下JMX配置:
set "JAVA_OPTS=%JAVA_OPTS%
-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.port=9090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
我不能再獲得詳細的內存堆轉儲,但內存配置文件看起來要好得多:
24小時后,內存配置文件看起來一樣:
我建議使用memoryAnalyzer來分析你的堆,它提供了更多的信息。
http://www.eclipse.org/mat/有一個獨立的應用程序和eclipse嵌入式應用程序。 你只需要在你的應用程序上運行jmap並用它來分析結果。
我可以推薦jvisualvm ,它隨每個Java安裝一起提供。 啟動該程序,連接到您的Web應用程序。 轉到監視器 - >堆轉儲 。 現在可能需要一些時間(取決於大小)。 通過堆轉儲導航很容易,但你必須弄清楚自己的意思(雖然不是太復雜),例如
轉到Classes (在heapdump中),選擇java.lang.String ,右鍵單擊Instances View中的Show 。 之后,您將在左側的表中看到系統中當前處於活動狀態的字符串實例。 單擊一個String實例,您將在右表的右上方看到一些String優先級 ,就像String的值一樣。
上你會看到在此 字符串實例從引用的右表的右下側部分。 在這里,您必須檢查引用大多數* String *的位置。 但是對於你的情況(176/210,很好找到一些很快就會導致問題的字符串示例),應該在一些檢查后明確問題所在。
高原是由可用內存降至默認百分比閾值以下導致Full GC引起的。 這解釋了為什么性能會隨着JVM在嘗試查找和釋放內存時不斷暫停而下降。
我通常會建議查看對象緩存,但在您的情況下,我認為您的堆大小對於Tomcat實例+ webapp來說太低了。 我建議將堆增加到1G( -Xms1024m -Xmx1024m
),然后再次檢查內存使用情況。
如果你仍然看到相同的行為,那么你應該采取另一個堆轉儲並查看String和Char之后的最大消費者。 根據我的經驗,這通常是緩存機制。 如果可能的話,要么進一步增加內存,要么減少緩存存儲。 有些緩存只定義了對象的數量,因此您需要了解每個緩存對象的大小。
一旦你了解你的內存使用情況,你可以再次降低它,但恕我直言512MB將是最低限度。
更新:
您無需擔心無法訪問的對象,因為它們應該由GC清理。 此外,按類型划分的最大消費者是String和Char是正常的 - 大多數對象都包含某種字符串,因此字符串和字符在頻率上是最常見的。 了解包含字符串的對象的內容是查找內存使用者的關鍵。
我剛剛在一個完全不同的應用程序中遇到了同樣的問題,所以tomcat7可能不應該受到責備。 內存分析器在進程中顯示10M無法訪問的String實例(已運行約2個月),並且大多數/所有實例都具有與垃圾收集相關的值(例如,“分配失敗”,“次要GC結束”)
內存分析器
Full GC現在每2秒運行一次,但這些字符串不會被收集。 我的猜測是我們遇到了GC代碼中的錯誤。 我們使用以下java版本:
$ java -version
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)
以及以下VM參數:
-Xms256m -Xmx768m -server -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:NewSize=32m -XX:MaxNewSize=64m
-XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
-Xloggc:/path/to/file
偶然的是,我偶然發現了Tomcat的conf / catalina.properties文件中的以下幾行,這些文件激活了字符串緩存。 如果您打開了任何一個,這可能與您的情況有關。 似乎其他人警告使用該功能。
tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000
由於這聽起來不明確,一個候選人應該是JSF。 但后來我也預料到哈希映射會泄漏。
你應該使用JSF:在web.xml中你可以嘗試:
至於工具: JavaMelody可能對持續統計很有意義,但需要付出努力。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.