尚硅谷2020最新版宋红康JVM教程 10 直接内存内容记录整理
目录
一、内容结构
二、直接内存概述
1. 为什么介绍直接内存?
- Java 中有许多地方使用直接内存,尤其是 JDK 8 之后,Metaspace 就使用直接内存实现。
- Java 进程的内存大小等于运行时数据区大小与直接内存大小之和。如果忽略直接内存,可能导致 OOM 。
2. 直接内存概述
- 直接内存不属于 JVM 运行时数据区,也不是 JVM 规范定义的区域。
- 直接内存来源于 NIO, 通过 DirectByteBuffer 操作本地内存。
- 访问直接内存效率比使用 Java 堆性能更高。
三、IO 与 NIO 简要介绍
1. IO 与 NIO
IO
是最初实现数据传输的规范
- 传输工具:字节数组(byte[])或字符数组(char[])
- 操作方式:字节流或字符流(Stream)
- 非直接内存访问,需要在用户态和内核态复制数据,效率较低。
NIO
JDK 1.4 中引入了 NIO,NIO 可以理解为 New IO 或 NoBlocked IO
- 传输工具:缓存(Buffer)
- 操作方式:通道(Channel)
- 操作系统划出的直接缓冲区,Java 程序可以直接访问,只有一份,效率较高。
2. 使用 NIO 分配和释放内存
/**
* 1G 缓存大小
*/
private static final int BUFF = 1024 * 1024 * 1024;
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(BUFF);
System.out.println("直接内存分配完毕,请求指示");
Scanner scanner = new Scanner(System.in);
//阻塞,可以通过系统任务管理器查看 Java 进程中内存使用情况
scanner.next();
//输入任意内存后回车继续
System.out.println("直接内存开始释放");
//释放内存
buffer = null;
System.gc();
//阻塞,再次查看内存使用情况
scanner.next();
}
3. 比较 IO 和 NIO 复制大文件
直接内存使用效率高于 Java 堆访问,在读写频繁的场合应考虑使用直接内存。
- 调用代码
long ioSum = 0, nioSum = 0;
String dir = "F:\\test\\";
for (int i = 0; i < 3; i++) {
ioSum += io(SRC, dir + "io" + i + ".mp4");
nioSum += directBuffer(SRC, dir + "direct" + i + ".mp4");
}
System.out.println("IO 总花费的时间为:" + ioSum); // 58463
System.out.println("NIO 总花费的时间为:" + nioSum); //51403
- 使用 IO 复制文件,用时 58643 毫秒
long start = System.currentTimeMillis();
FileInputStream inputStream = new FileInputStream(src);
FileOutputStream outputStream = new FileOutputStream(dest);
byte[] buffer = new byte[_100MB];
int len = 0;
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
outputStream.close();
return System.currentTimeMillis() - start;
- 使用 NIO 复制文件,用时 51403 毫秒
FileChannel inChannel, outChannel;
inChannel = new FileInputStream(src).getChannel();
outChannel = new FileOutputStream(dest).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(_100MB);
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
inChannel.close();
outChannel.close();
return System.currentTimeMillis() - start;
四、直接内存设置和垃圾回收
1. Java 直接内存大小设置
- 通过 JVM 参数 -XX:MaxDirectMemorySize=xx 设置使用直接内存大小,默认与 -Xmx (堆最大值)相同。
- Java 进程使用的内存大小约等于堆内存大小与使用的直接内存大小之和
2. Java 直接内存与垃圾回收
直接内存回收成本比较高,且不受 JVM 垃圾回收管理。
- 使用 JProfile、JVisual 等 JVM 性能分析工具无法查看到直接内存使用情况。
- Dump 文件也没有直接内存使用情况记录。
直接内存的 OOM
如果直接内存使用超过最大值或系统限制,会抛出 OutOfMemoryError:Direct buffer Memory
- 直接内存 OOM 示例
List<ByteBuffer> bufferList = new ArrayList<>();
while(true){
ByteBuffer allocate = ByteBuffer.allocateDirect(_1M);
bufferList.add(allocate);
}
设置直接内存大小: -XX:MaxDirectMemorySize=10m,输出结果如下:
Exception in thread “main” java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:694)
at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at chapter10.BufferOomTest.main(BufferOomTest.java:23)
附件
PPT 的没有。。。