簡體   English   中英

java8“java.lang.OutOfMemoryError:元空間”

[英]java8 “java.lang.OutOfMemoryError: Metaspace”

在將我們的 Java 應用程序(在 Tomcat 上運行的服務)JRE 從 Java 7 切換到 Java 8 后,我們開始看到java.lang.OutOfMemoryError: Metaspace在高流量運行幾天后。

堆使用正常。 在性能測試期間執行相同的代碼流一段時間后,元空間會跳轉。

元空間內存問題的可能原因是什么?

當前設置是:

-server -Xms8g -Xmx8g -XX:MaxMetaspaceSize=3200m  -XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC -XX:MaxGCPauseMillis=1000 
-XX:+DisableExplicitGC -XX:+PrintGCDetails 
-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=7 -XX:NewSize=5004m 
-XX:MaxNewSize=5004m -XX:MaxTenuringThreshold=12 
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintFlagsFinal  
-XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution 
-XX:+PrintGCCause -XX:+PrintAdaptiveSizePolicy 
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=200M 

此外,該應用程序大量使用反射。 我們還使用自定義類加載器。 所有這些都在 java 7 中正常工作。

我假設您可以在一段時間內使用相同的請求(請求集)創建問題。 定義 MaxMetaspaceSize 是一件好事,否則應用程序將使用本機內存,直到它用完為止。 但我將從以下步驟開始:

  1. 當您多次將同一請求發送到服務器時,請檢查在 JVM 中加載的類的數量是否不斷增加。 如果是,您可能正在創建動態類,這會導致元空間中加載的類增加。 那么如何查看加載的類的數量,可以使用visualvm使用JMX連接服務器或者本地運行模擬。 我將提到本地的步驟,但對於遠程附加 JMX,您應該將以下內容添加到應用程序的 JVM 參數並啟動它並在端口 9999 和 -XX:+UnlockDiagnosticVMOptions 上遠程連接。
 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -XX:+UnlockDiagnosticVMOptions

一旦將visualvm(jvisualvm)連接到JVM,點擊monitor,然后查看加載的類的數量。 在那里您可以監視堆以及元空間。 但我會添加其他工具來密切監視元空間。

  1. 此外,一旦您連接到 jvm,您可能需要拍攝堆快照並找出使用 OQL 加載的類。 因此,在進行堆轉儲之前,請停止對服務器的請求,這樣您就不會捕獲任何進行中的請求/執行代碼及其相關對象,但這不是必需的。 因此,在多次運行同一組請求后,在 visualvm 中,在“監視器”空間中,單擊右上角的“堆轉儲”。然后打開/加載快照,您將看到 OQL 控制台的選項。和您會在 permgen 分析下的右下面板上看到一些預定義的 OQL 查詢。運行名為“類加載器加載的類直方圖”的查詢,我想這將給出每個類加載器加載的類的數量。您可以使用它來找出哪個類加載器正在加載類。

select map(sort(map(heap.objects('java.lang.ClassLoader'), '{loader: it, count: it.classes.elementCount }'), 'lhs.count < rhs.count'), 'toHtml (它) + "
"')

但是上面名為“類加載器加載的類”的查詢會很慢,它實際上會顯示每個類加載器加載的類。

select { loader: cl,
             classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
    from instanceof java.lang.ClassLoader cl
  1. 然后嘗試追蹤元空間區域的增長。 現在我們將使用 jconsole 和 java 有的新東西:jmc(java 任務控制)。 您可以使用 jconsole 連接到 jvm(本地或遠程),一旦連接到內存選項卡,您就可以監控那里的非堆增長,它應該有元空間和代碼緩存以及壓縮的類空間。 現在連接

JMC

連接到虛擬機,然后在連接后單擊右上角的 JMC 中的“診斷命令”。 由於我們啟用了 UnlockDiagnosticVMOptions ,因此可以執行 GC.class_stats 。 您可能希望通過顯示所有列運行它並在 csv 中打印。 所以命令看起來像:

GC.class_stats -all=true -csv=true

然后你可以比較不同時期的類統計數據,找出哪些類導致問題(元空間增長)或哪些類在元空間中有相關信息(方法/方法數據)。 如何分析當時收集的 csv 輸出:好吧,我會將該 csv 加載到數據庫或其他地方的兩個類似表(代表 csv)中,以比較 GC.class_stats csv 輸出,我可以在其中運行一些 SQL 或任何其他分析工具。 這樣可以更好地了解元空間中究竟增長了什么。 GC 類統計信息包含以下列:

指數,超級,InstSize,InstCount,InstBytes,鏡子,KlassBytes,K_secondary_supers,VTab,ITAB,OopMap,IK_methods,IK_method_ordering,IK_default_methods,IK_default_vtable_indices,IK_local_interfaces,IK_transitive_interfaces,IK_fields,IK_inner_classes,IK_signers,class_annotations,class_type_annotations,fields_annotations,fields_type_annotations,methods_annotations, methods_parameter_annotations,methods_type_annotations,methods_default_annotations,annotations,Cp,CpTags,CpCache,CpOperands,CpRefMap,CpAll,MethodCount,MethodBytes,ConstMethod,MethodData,StackMap,Bytecodes,MethodAll,ROAll,RWAll,Loader,ClassName,Class

希望它有幫助。 此外,如果它不會在 1.7 中導致任何泄漏,則該錯誤可能出現在 Java 8 中。

如果任何人持有對類加載器的任何引用,這些類也不會從元空間中卸載。 如果你知道你的類加載器應該被 GC 並且沒有人應該持有對你的類加載器的引用,你可以回到visualvm 中的堆轉儲並單擊類加載器實例並右鍵單擊以找到“最近的 GC 根”,它會告訴你您持有對類加載器的引用。

我們遇到了類似的問題,根本原因是 60K 類文件被加載到元空間內存中,但沒有被卸載。在 JVM arg 下面添加修復了問題。

-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true

https://issues.apache.org/jira/browse/CXF-2939

希望這會有所幫助。

在將Java應用程序(在Tomcat上運行的服務)的Java應用程序從Java 7切換到Java 8之后,在運行了幾天的高流量之后,我們開始看到java.lang.OutOfMemoryError: Metaspace

堆的使用還可以。 在性能測試期間執行相同代碼流之后的某個時間之后,元空間會跳轉。

元空間內存問題的可能原因是什么?

當前設置為:

-server -Xms8g -Xmx8g -XX:MaxMetaspaceSize=3200m  -XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC -XX:MaxGCPauseMillis=1000 
-XX:+DisableExplicitGC -XX:+PrintGCDetails 
-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=7 -XX:NewSize=5004m 
-XX:MaxNewSize=5004m -XX:MaxTenuringThreshold=12 
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintFlagsFinal  
-XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution 
-XX:+PrintGCCause -XX:+PrintAdaptiveSizePolicy 
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=200M 

該應用程序也大量使用反射。 此外,我們使用自定義類加載器。 他們都在Java 7中正常工作。

暫無
暫無

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

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