簡體   English   中英

Java Web應用程序中的內存泄漏

[英]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中你可以嘗試:

  • javax.faces.STATE_SAVING_METHOD 客戶端
  • com.sun.faces.numberOfViewsInSession 0
  • com.sun.faces.numberOfLogicalViews 1

至於工具: JavaMelody可能對持續統計很有意義,但需要付出努力。

嘗試使用MAT並確保在解析heapdump時,不要刪除無法訪問的對象。

為此,請按照此處的教程進行操作。

然后你可以運行一個簡單的Mem Leak Analysis( 是一個很好的教程)

這應該很快引導你找到根本原因。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM