Java OutOfMemoryError Java Heap Space - Java 147

Java OutOfMemoryError Java Heap Space – Java 147

Heap Space 用於存儲物件實例,只要不斷地建立物件,隨著物件數量的增加,總容量超過最大容量限制後,就會產生 Java OutOfMemoryError 的錯誤,模擬建立一個大小 100MB 的 byte 陣列,將其加入到一個 List 中,然後重複建立和加入過程,直到 JVM 拋出錯誤, 本篇增加了範例,並透過單元測試來驗證產出結果。

Java OutOfMemoryError

Java Heap Space

Java 堆記憶體,設定 JVM Arguments。
-Xmn1g -Xms4g -Xmx4g

檔案目錄

./
   +- src
       +- test
       |   +- org
       |       +- ruoxue
       |           +- java_147
       |               +- memory
       |                   +- JavaHeapSpaceTest.java 

Out of memory

有以下幾種情境:

Array

建立一個超大陣列。

Request

超乎預期的訪問量或資料,通常是上游系統請求流量飆升,常見於各類促銷活動、返鄉搶票。

Finalizer

過度使用最終終結器,物件沒有立即被 GC 。

Memory leak

引用大量物件沒有被釋放,JVM 無法對其自動回收,常見於使用了文件等資源沒有回收等。

測試 JUnit 4

list

建立一個大小 100MB 的 byte array ,將其加入到一個 List 中。

	@Test
	public void list() {
		List<byte[]> list = new ArrayList<byte[]>();
		int counter = 1;
		for (;;) {
			byte[] bytes = new byte[100 * 1024 * 1024];
			list.add(bytes);
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}
[32] free memory: 744506928
[33] free memory: 639649312
[34] free memory: 534791696
[35] free memory: 423131936
[36] free memory: 318274320

java.lang.OutOfMemoryError: Java heap space
	at org.ruoxue.java_147.memory.HeapSpaceTest.list(HeapSpaceTest.java:15)

integer

建立一個整數,將其加入到一個 List 中。

	@Test
	public void integer() {
		List<Integer> list = new ArrayList<Integer>();
		int counter = 1;
		for (;;) {
			list.add(counter);
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}
[1215483] free memory: 1440968
[1215484] free memory: 1440968
[1215485] free memory: 1440968
[1215486] free memory: 1440968
[1215487] free memory: 1440968

java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:267)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
	at java.util.ArrayList.add(ArrayList.java:464)
	at org.ruoxue.java_147.memory.HeapSpaceTest.integer(HeapSpaceTest.java:27)	

bytearray

建立一個大小 100MB 的 byte array ,重複建立過程。

	@Test
	public void byteArray() {
		createBytes();
	}

	public void createBytes() {
		Byte[] bytes = null;
		int counter = 1;
		for (;;) {
			bytes = new Byte[100 * 1024 * 1024];
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}
[1] free memory: 854589312

java.lang.OutOfMemoryError: Java heap space
	at org.ruoxue.java_147.memory.HeapSpaceTest.createBytes(HeapSpaceTest.java:42)
	at org.ruoxue.java_147.memory.HeapSpaceTest.byteArray(HeapSpaceTest.java:35)

normal

建立一個大小 300MB 的 byte array ,重複建立過程,觸發 GC 回收,記憶體一直保持在可用空間,並不會產生記憶體不足的錯誤。

	@Test
	public void normal() {
		int counter = 1;
		for (;;) {
			Byte[] bytes = new Byte[300 * 1024 * 1024];
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}
[GC (Allocation Failure)  3713819K->2458379K(4193792K), 0.2385952 secs]
[122] free memory: 491491712
[GC (Allocation Failure)  3713819K->2458379K(4193792K), 0.2319988 secs]
[123] free memory: 491491712
[GC (Allocation Failure)  3713819K->2458379K(4193792K), 0.2272807 secs]
[124] free memory: 491491712
[GC (Allocation Failure)  3713819K->2458379K(4193792K), 0.2303681 secs]
[125] free memory: 491491712

JavaHeapSpaceTest.java

package org.ruoxue.java_147.memory;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class JavaHeapSpaceTest {

	@Test
	public void list() {
		List<byte[]> list = new ArrayList<byte[]>();
		int counter = 1;
		for (;;) {
			byte[] bytes = new byte[100 * 1024 * 1024];
			list.add(bytes);
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}

	@Test
	public void integer() {
		List<Integer> list = new ArrayList<Integer>();
		int counter = 1;
		for (;;) {
			list.add(counter);
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}

	@Test
	public void byteArray() {
		createBytes();
	}

	public void createBytes() {
		Byte[] bytes = null;
		int counter = 1;
		for (;;) {
			bytes = new Byte[100 * 1024 * 1024];
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}

	@Test
	public void normal() {
		int counter = 1;
		for (;;) {
			Byte[] bytes = new Byte[300 * 1024 * 1024];
			Runtime rt = Runtime.getRuntime();
			System.out.printf("[%d] free memory: %s%n", counter++, rt.freeMemory());
		}
	}
}

心得分享

Increase Java Heap Space

-Xmn1g -Xms4g -Xmx4g

實際大部分情況,通常只需要通過 -Xmx 參數調高 Heap Space 記憶體空間即可,如果仍然沒有解決,參考以下情況作進一步調整:

  1. 若是超大物件,可以檢查其合理性,比如是否一次性查詢了資料庫全部結果,而沒有做結果數限制。
  2. 若是業務峰值壓力,可以考慮添加機器資源,或者做限流降級。
  3. 若是記憶體不足,需要找到持有的物件,修改程式碼設計,例如關閉沒有釋放的連接。

發佈留言