画图学 JVM(十二)10 直接内存

尚硅谷2020最新版宋红康JVM教程 10 直接内存内容记录整理

一、内容结构

在这里插入图片描述

二、直接内存概述

1. 为什么介绍直接内存?

  1. Java 中有许多地方使用直接内存,尤其是 JDK 8 之后,Metaspace 就使用直接内存实现。
  2. Java 进程的内存大小等于运行时数据区大小与直接内存大小之和。如果忽略直接内存,可能导致 OOM 。

2. 直接内存概述

  1. 直接内存不属于 JVM 运行时数据区,也不是 JVM 规范定义的区域。
  2. 直接内存来源于 NIO, 通过 DirectByteBuffer 操作本地内存。
  3. 访问直接内存效率比使用 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)

附件

看图学 JVM 目录

PPT 的没有。。。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值