JVM 堆(heap)溢出案例

1、說明

當虛擬機申請不到內存空間的時候,會報堆內存溢出: OutOfMemoryError:java heap spacejava

常見的緣由: http://outofmemory.cn/c/java-...

我測試到時候,運行在 16G 內存的機器上。JVM 堆內存 默認爲物理內存的1/4,即 16 * 1/4 = 4G數據庫

JDK 8的 JVM 在 JDK 7 的基礎上從堆內存中移除了 永久代(Perm Generation),替換爲了堆內存以外的 元空間(Metaspace),元空間是堆外直接內存,不受堆內存的限制,只受物理內存的限制,能夠提供更大的空間。

2、緣由及解決辦法

OutOfMemoryError 異常的常見緣由:數組

  1. 加載的數據過大。如:加載的文件或者圖片過大、一次從數據庫取出過多數據
  2. 代碼存在死循環或循環產生過多的對象

解決方法jvm

  1. 增長jvm的內存大小,使用 -Xmx 和 -Xms 來設置
  2. 檢查代碼中是否有死循環或遞歸調用。
  3. 檢查是否有大循環重複產生新對象實體。
  4. 檢查對數據庫查詢中,是否有一次得到所有數據的查詢。通常來講,若是一次取十萬條記錄到內存,就可能引發內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線後,數據庫中數據多了,一次查詢就有可能引發內存溢出。所以對於數據庫查詢儘可能採用分頁的方式查詢。
  5. 檢查List、Map等集合對象是否有使用完後,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。

3、代碼示例

/**java堆溢出實例
 * 原理:java的堆是用來存放對象實例的,因此咱們只要作到如下三點就可使堆溢出:
 * 一、限制堆的大小,不可擴展
 * 二、不斷新建對象
 * 三、保持對象存活不被回收
 * 對應的,咱們須要:
 * 一、改變JVM的啓動參數,將堆的最小值和最大值設成同樣,這樣就能夠避免堆自動擴展(其實不同也能夠)
 * 二、不斷產生對象
 * 三、使用一個List來保存對象,保持對象存活
 *
 * JVM配置參數: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 *
 */
public class HeapOom {
    public static void main(String[] args) {
        // 此list實例會存放在堆內存中
        List<byte[]> list = new ArrayList<>();
        int i = 0;
        boolean flag = true;
        while (flag) {
            try {
                i++;
                // 每次增長一個1M大小的數組對象
                list.add(new byte[1024 * 1024]);
            } catch (Throwable e) {  // catch 捕獲的是 Throwable,而不是 Exception。由於 OutOfMemoryError 不屬於 Exception 的子類。
                e.printStackTrace();
                flag = false;
                // 記錄次數
                System.out.println("count=" + i);
            }
        }

        // 不讓進程結束,便於使用分析工具來查看內存狀況
        try {
            Thread.sleep(24 * 60 * 60 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使用的 java 1.8.0_171 版本:工具

$ java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

報錯信息:測試

count=3316
java.lang.OutOfMemoryError: Java heap space
    at com.song.HeapOom.main(HeapOom.java:21)

運行結果代表,運行到 3316 次時,出現了堆內存溢出。因爲每次增長1M內存。粗略估計:程序大約使用3G內存時,出現了內存溢出狀況。ui

經過jmap或VisualVM 者工具能夠查看內存狀況:最後能夠看出,老年代內存使用狀況,大約使用了2709MB,使用率近100%。從而致使了 OutOfMemoryErrorspa

TIPS:下面查看內存時,因爲使用工具查看內存的時間不是同一時間,因此內存使用量有細微差異

1. 使用jmap查看的內存信息

$ jmap -heap 3428
Attaching to process ID 3428, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b18

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4261412864 (4064.0MB)
   NewSize                  = 88604672 (84.5MB)
   MaxNewSize               = 1420296192 (1354.5MB)
   OldSize                  = 177733632 (169.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 637009920 (607.5MB)
   used     = 637009920 (607.5MB)
   free     = 0 (0.0MB)
   100.0% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation  // 老年代內存使用狀況,大約使用了2709MB,使用率近100%(致使OutOfMemoryError)
   capacity = 2841116672 (2709.5MB)
   used     = 2840991320 (2709.38045501709MB)
   free     = 125352 (0.11954498291015625MB)
   99.99558793198338% used

4955 interned Strings occupying 422328 bytes.

2. 使用 VisualVM 查看的內存信息

clipboard.png

clipboard.png

clipboard.png

相關文章
相關標籤/搜索