直接内存(Direct Memory)


概述

  • 在 jdk8 的时候引入了元空间,而元空间使用的就是直接内存。

  • 直接内存不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

  • 直接内存是在 Java 堆外的、直接向系统申请的内存区间。

  • 来源于 NIO (其实在 jdk1.4 引入 NIO 时就有了操作本地内存的概念,后来在 jdk1.7 时引入了 NIO2),通过存在堆中的DirectByteBuffer 操作 Native 内存

  • 通常,访问直接内存的速度会优于 Java 堆。即读写性能高。

    • 因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。
    • Java 的 NIO 库允许 Java 程序使用直接内存,用于数据缓冲区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* IO NIO (New IO / Non-Blocking IO)
* byte[] / char[] Buffer
* Stream Channel
*
* 查看直接内存的占用与释放
*/
public class BufferTest {
private static final int BUFFER = 1024 * 1024 * 1024;//1GB

public static void main(String[] args){
//直接分配本地内存空间
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
System.out.println("直接内存分配完毕,请求指示!");

Scanner scanner = new Scanner(System.in);
scanner.next();

System.out.println("直接内存开始释放!");
byteBuffer = null;
System.gc();
scanner.next();
}
}

直接占用了1G的本地内存

image-20220720164700347

当控制台输入”释放内存”后,占用内存明显减少

image-20220720164913719

BIO与NIO

非直接缓存区(BIO)

  • 原来采用BIO的架构,在读写本地文件时,我们需要从用户态切换成内核态img

直接缓冲区(NIO)

  • NIO 直接操作物理磁盘,省去了中间过程

img

如何理解上面的内容:我们的java程序,想要操作本地磁盘,首先命令会到用户地址空间(jvm中),然后这个jvm映射会到内核地址空间(这个应该是操作系统提供的映射物理磁盘的),最终到物理磁盘。而NIO直接操作一个映射文件,是java程序直接到物理磁盘的。

直接内存与OOM

  • 直接内存也可能导致 OutofMemoryError 异常
  • 由于直接内存在 Java 堆外,因此它的大小不会直接受限于-Xmx指定的最大堆大小,但是系统内存是有限的,Java 堆和直接内存的总和依然受限于操作系统能给出的最大内存。
  • 直接内存的缺点为:
    • 分配回收成本较高
    • 不受 JVM 内存回收管理
  • 直接内存大小可以通过MaxDirectMemorySize设置
  • 如果不指定,默认与堆的最大值-Xmx参数值一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 本地内存的OOM: OutOfMemoryError: Direct buffer memory
*
*/
public class BufferTest2 {
private static final int BUFFER = 1024 * 1024 * 20;//20MB

public static void main(String[] args) {
ArrayList<ByteBuffer> list = new ArrayList<>();

int count = 0;
try {
while(true){ // 不断使用
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER); // 使用本地内存
list.add(byteBuffer);
count++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
System.out.println(count);
}


}
}

输出结果

1
2
3
4
5
6
181
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:694)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at com.ysy.methodarea.BufferTest2.main(BufferTest2.java:19)

文章作者: Yang Shiyu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Yang Shiyu !
  目录