Heap Space 用於存儲物件實例,只要不斷地建立物件,隨著物件數量的增加,總容量超過最大容量限制後,就會產生 Java OutOfMemoryError 的錯誤,模擬建立一個大小 100MB 的 byte 陣列,將其加入到一個 List 中,然後重複建立和加入過程,直到 JVM 拋出錯誤, 本篇增加了範例,並透過單元測試來驗證產出結果。
Table of Contents
ToggleJava 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 記憶體空間即可,如果仍然沒有解決,參考以下情況作進一步調整:
- 若是超大物件,可以檢查其合理性,比如是否一次性查詢了資料庫全部結果,而沒有做結果數限制。
- 若是業務峰值壓力,可以考慮添加機器資源,或者做限流降級。
- 若是記憶體不足,需要找到持有的物件,修改程式碼設計,例如關閉沒有釋放的連接。