一、直接内存概述
1、不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。
2、直接内存是在Java堆外的、直接向系统申请的内存区间;
3、来源于NIO,通过存在堆中的 DirectByteBuffer 操作 Native 内存;
4、通常,访问直接内存的速度会优于 Java 堆。即读写性能高;
因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存;
Java的NIO库允许Java程序使用直接内存,用于数据缓冲区。
直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,它直接从操作系统中分配,因此不受Java堆大小的限制,但是会受到本机总内存的大小及处理器寻址空间的限制,因此它也可能导致OutOfMemoryError异常出现。在JDK1.4中新引入了NIO机制,它是一种基于通道与缓冲区的新I/O方式,可以直接从操作系统中分配直接内存,即在堆外分配内存,这样能在一些场景中提高性能,因为避免了在Java堆和Native堆中来回复制数据。
二、访问直接内存的速度会优于 Java 堆
1、非直接缓冲区(传统IO)
读写文件,需要与磁盘交互,需要由用户态切换到内核态。在内核态时,需要内存如上图的操作。
使用IO,见上图。这里需要两份内存存储重复数据,效率低。
2、直接缓冲区(NIO)
使用NIO时,如上图。操作系统划出的直接缓存区可以被 java 代码直接访问,只有一份。NIO适合对大文件的读写操作。
因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存;
代码示例:
1 public class BufferTest {
2 private static final int BUFFER = 1024 * 1024 * 1024;//1GB
3
4 public static void main(String[] args){
5 //直接分配本地内存空间
6 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
7 System.out.println("直接内存分配完毕,请求指示!");
8
9 Scanner scanner = new Scanner(System.in);
10 scanner.next();
11
12
13 System.out.println("直接内存开始释放!");
14 byteBuffer = null;
15 System.gc();
16 scanner.next();
17 }
18 }
三、直接内存的OOM与大小设置
1、也可能导致outofMemoryError异常(Direct buffer memory)
2、由于直接内存在Java堆外,因此它的大小不会直接受限于-Xmx 指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。
3、缺点
① 分配回收成本较高
② 不受 JVM 内存回收管理
4、直接内存大小可以通过MaxDirectMemorysize设置
5、如果不指定,默认与堆的最大值 -Xmx 参数值一致
设置本地内存:
1 本地内存的OOM: OutOfMemoryError: Direct buffer memory
2 public class BufferTest2 {
3 private static final int BUFFER = 1024 * 1024 * 20;//20MB
4
5 public static void main(String[] args) {
6 ArrayList<ByteBuffer> list = new ArrayList<>();
7
8 int count = 0;
9 try {
10 while(true){
11 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
12 list.add(byteBuffer);
13 count++;
14 try {
15 Thread.sleep(100);
16 } catch (InterruptedException e) {
17 e.printStackTrace();
18 }
19 }
20 } finally {
21 System.out.println(count);
22 }
23
24
25 }
26 }
简单理解:
Java process memory = java heap + native memory
更多: