當使用 NIO 的時候經常需要使用 ByteBuffer 來讀取或寫入數據,這是一種基於 Channel (通道) 和 Buffer (緩衝區)的 I/O 方式,它可以使用 Native 函數庫直接分配堆外內存,然後通過一個存儲在 Java 堆裡面的 DirectByteBuffer 對像作為這塊內存的引用進行操作,這樣在一些場景就避免了 Java 堆和 Native 中來回複製數據,所以性能會有所提高, Java 允許應用程序通過 Direct ByteBuffer 直接訪問堆外內存,許多高性能程序通過 Direct ByteBuffer 結合內存映射文件(Memory Mapped File)實現高速 IO。
Table of Contents
ToggleJava OutOfMemoryError
Direct Buffer Memory
Java 堆記憶體,設定 JVM Arguments。
-Xmn1g -Xms4g -Xmx4g
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- memory
| +- DirectBufferMemoryTest.java
Out of memory
如果設置的 Head Space 特別小,當建立一個超大陣列,往往很容易拋出 Java Heap Space 的錯誤。
測試 JUnit 4
Java heap space
Java 堆記憶體,建立一個超大的 ByteBuffer 。
@Test
public void allocate() {
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
ByteBuffer buffer = ByteBuffer.allocate(Integer.MAX_VALUE - 2);
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
}
freeMemory: 3921 MB
freeMemory: 1873 MB
Buffered Memory
本機記憶體,建立一個超大的 ByteBuffer 。
@Test
public void allocateDirect() {
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
ByteBuffer buffer = ByteBuffer.allocateDirect(Integer.MAX_VALUE);
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
}
java.lang.OutOfMemoryError: Direct buffer memory
DirectBufferMemoryTest.java
package org.ruoxue.java_147.memory;
import java.nio.ByteBuffer;
import org.junit.Test;
public class DirectBufferMemoryTest {
@Test
public void allocate() {
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
ByteBuffer buffer = ByteBuffer.allocate(Integer.MAX_VALUE - 2);
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
}
@Test
public void allocateDirect() {
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
ByteBuffer buffer = ByteBuffer.allocateDirect(Integer.MAX_VALUE);
System.out.println("freeMemory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
}
}
心得分享
Java 只能通過 ByteBuffer.allocateDirect 方法使用 Direct ByteBuffer,參考以下情況作進一步調整:
- 檢查是否直接或間接使用了 NIO,如 netty,jetty 等。
- 通過啟動參數 -XX:MaxDirectMemorySize 調整 Direct ByteBuffer 的上限值。
- 檢查 JVM 參數是否有 -XX:+DisableExplicitGC 選項,如果有就去掉,因為該參數會使 System.gc() 失效。
- 檢查堆外內存使用代碼,確認是否存在內存洩漏;或者通過反射調用 sun.misc.Cleaner的 clean() 方法來主動釋放被 Direct ByteBuffer 持有的內存空間。
- 內存容量確實不足,升級配置。