这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」
概念
直接内存英文为Direct Memory ,它并不是虚拟机运行时数据区的一部分,也不是jvm规范中定义的内存区域。
常见的NIO(基于通道(Channel)与缓冲区 (Buffer)的I/O方式)操作时,用于数据缓冲区,如 BufferedOutputStream
由于是对操作系统操作,所以分配回收成较高,但读写性能高
不受jvm内存回收管理但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。
案例
static final String FROM = "D:\下载\20211017.mp4";
static final String TO = "D:\lol.mp4";
static final int ONE_MB = 1024 * 1024;
public static void main(String[] args){
io();
buffer();
}
public static void buffer(){
long startAt = System.nanoTime();
try(FileChannel from = new FileInputStream(FROM).getChannel();
FileChannel to = new FileOutputStream(TO).getChannel())
{
ByteBuffer bBuf = ByteBuffer.allocateDirect(ONE_MB);
while(true){
int len = from.read(bBuf);
if(len == -1){
break;
}
bBuf.flip();
to.write(bBuf);
bBuf.clear();
}
}catch(IOException e){
e.printStackTrace();
}
long endAt = System.nanoTime();
System.out.println("io用时:" + (endAt - startAt));
}
public static void io(){
long startAt = System.nanoTime();
try(FileInputStream from = new FileInputStream(FROM);
FileOutputStream to = new FileOutputStream(TO))
{
byte[] buf = new byte[ONE_MB];
while(true){
int len = from.read(buf);
if(len == -1){
break;
}
to.write(buf,0,len);
}
}catch(IOException e){
e.printStackTrace();
}
long endAt = System.nanoTime();
System.out.println("directBuffer 用时:" + (endAt - startAt));
}
复制代码
运行以上代码后会发现传统 io 的用时会明显要慢于 使用了缓存的 buffer 方法,也就是使用了Direct Memory 的方法。
原因
传统io
由于java语言本身不具备磁盘的读写能力,它需要操作磁盘需要调用操作系统提供的方法也就是本地方法。
(在调用操作系统提供的方法时cpu会从java的用户态切换为内核态,当切换为内核态的时候由计算机系统或cpu去读取磁盘文件内容,读取的时候会分词进行读取到系统缓存区中,当然系统缓冲区对于java程序来说也还是不能进行直接操作的,这个时候java程序会在堆内存中分配一块java的缓冲区,这块内存java程序就可以使用了,调用完之后又会从内核态切换为用户态进行Java程序之后的操作进行文件写入)
由于会先读取到系统缓冲区然后再读取到java的缓冲区,做了一个不必要的数据复制,因而效率不会很高
直接内存(Direct Memory)
在 buffer 方法中使用了ByteBuffer.allocateDirect,就是说分配了一个直接内存,这个方法调用了之后表示在操作系统中划出了一个为 1M 大小的缓冲区供当前使用。这块区域对于Java程序来说是可以直接访问的,java程序可以使用,计算机系统也可以进行使用是一块共享的区域。
使用了直接内存就比传统io少了一次内存复制的操作,效率也就会快。




近期评论