原文:https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html
內存泄漏的常見現象就是java.lang.OutOfMemoryError異常。這個異常常見拋出的情況爲:當需要分配對象的時候,堆空間不足。在這種情況下,垃圾收集器無法獲取一個有效的空間來容納一個新的對象,同時堆的大小也無法進行擴展。
另外,這種錯誤也有可能發生在native memory(操作系統內存)沒有足夠的空間裝載一個java class的情況.還有一個比較少見的情況,當垃圾收集器耗費了大量的時間,但是僅僅只有很少的內存被釋放。
當一個 java.lang.OutOfMemoryError異常被拋出,相應的堆棧信息也會打印出來。
java.lang.OutOfMemoryError也可能在native library code請求分配空間時,空間不足的情況下發生(例如,交換空間不足(swap space))
診斷一個java.lang.OutOfMemoryError的異常最先要做的是確定異常產生的原因,到底是因爲java堆滿了還是因爲native堆滿了?
- Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space
- 造成的原因:java無法給新的對象分配空間。這種錯誤不一定是內存泄漏造成的。
- 可能是配置原因:當應用被設置了一個特定的堆容量(或者是默認的容量),但是這個容量又不足以支撐這個應用。
- 可能是對象的引用一直未釋放,這使它在垃圾回收的時候無法被清理。
- 過度使用finalizers。當一個類擁有一個finalize 方法,GC的時候該類型的對象不會被垃圾回收器清理掉。相反的是,垃圾回收完畢後,這個對象會進入隊列等待被回收,這個回收操作會發生在後面不確定的時間。在Oracle Sun的實現中,回收是由一個守護線程來執行的,該守護線程來處理隊列中的的對象 。當回收線程的處理速度跟不上對象入隊的速度的時候,就可能會導致堆內存被佔滿,從而拋出異常。還有一種情況是,當應用創建一個高優先級的線程(注:守護線程的優先級低),這會導致隊列中對象增長的速度大於守護線程處理的速度
- 造成的原因:java無法給新的對象分配空間。這種錯誤不一定是內存泄漏造成的。
- Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded(GC 開銷超出限額了)
- 造成的原因:當完成一次垃圾收集,如果程序花費了98%的時間在垃圾回收上,但是回收的堆空間小於2%,並且已經做過5次連續垃圾回收的操作,然後這個異常將會被拋出。這個異常通常情況下拋出的原因是:存活的數據幾乎佔滿了java堆的空間導致空閒的空間太小。
- 解決方法:增大堆的空間。該異常可以通過命令行進行關閉: -XX:-UseGCOverheadLimit(注:關閉這個參數僅僅是不拋出這種異常,而不是不拋異常)
- Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit (請求分配的數組大小超過vm的限制)
- 例如:當應用需要分配一個512M的數組,但是最大堆內存只有256M
- 解決方法
- 擴大堆內存
- 可能是程序有bug,要檢查請求分配的數組大小是否合理
- Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace
- 造成的原因:metaspace中class metadata相關的空間被耗盡。metaspace中class metadata的空間受限於MaxMetaSpaceSize。當class metadata所需要的內存超過MaxMetaSpaceSize,該異常會拋出
- 解決方法:
- 增大MaxMetaSpaceSize
- 由於MetaSpace和java堆都是分配在相同的地址空間中(注:無論是堆還是非堆,大家都是使用的同一個物理機器上的內存),當減少Java堆的容量的時候,MetaSpace將獲得更多的使用空間。如果Java堆有多餘的空間,這是一個折中的方法。
- Exception in thread thread_name: java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
- 造成的原因:native內存分配失敗或者接近耗盡,一般是發起內存分配的模塊拋出這個異常。
- 解決方法:當該異常信息拋出的時候,VM會調用fatal error(致命錯誤)處理機制(生成一個包含線程、進程、系統信息的日誌文件)。在native內存耗盡的情況下,日誌中的堆內存以及內存映射信息(memory map)可能是有用的。
- Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space
- 造成的原因:在64位平臺上一個class metadata 的指針可以用32位來表示(通過UseCompressedOops).它是通過UseCompressedClassPointers命令來控制的。當UseCompressedClassPointers使用時,classmetadata的可用內存將等於CompressedClassSpaceSize。當UseCompressedClassPointers所需要的空間超過了CompressedClassSpaceSize,這個異常就會被拋出。
- 解決方法:增大CompressedClassSpaceSize,關閉UseCompressedClassPointers。備註:CompressedClassSpaceSize的是有一個限定範圍的。比如說 -XX:CompressedClassSpaceSize=4g,超出了限制的範圍,將會返回一個 CompressedClassSpaceSize of 4294967296 is invalid; must be between 1048576 and 3221225472(CompressedClassSpaceSize 的參數4294967296 是無效的;必須在1048576和3221225472之間(換算一下是1M到3G之間))
- 備註:有多種類型的class metadata,包括kclass metadata 和其他 metadata。只有klass metadata 是存放在CompressedClassSpaceSize限定的空間裏。其他metadata存放在Metaspace
- Exception in thread thread_name: java.lang.OutOfMemoryError: reason stack_trace_with_native_method
- 造成的原因:當錯誤堆棧的棧頂是一個native方法,這表明native方法內存分配失敗。這與其他錯誤信息不同的是,它是在JNI或者native方法中被檢測出的這個異常,而不是在JVM代碼層面。
- 解決方法:你需要通過操作系統的一些本地工具來做進一步診斷