理解內存溢出

原文: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堆滿了?

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