概念
零拷贝作为文件拷贝技术,它是指的一类技术,而不是一技术框架之类的东西,它通常的实现方式有两种,一个是DMA(direct memory access)翻译过来叫直接内存访问,是一种硬件层面的技术,它的原理是在进行文件拷贝时不需要cpu的介入,可以直接把文件从内存里面拷贝到另一个硬件中,比如磁盘、或者网卡中,另一种技术就是MMAP(Memory Mapping)是一种将文件或其他资源映射到进程的地址空间的技术,使得进程可以像访问普通内存一样访问这些资源,它则是属于软件层面的技术,相比于传统的文件拷贝需要CPU的介入,文件数据需要再内核空间和用户空间拷贝几次,零拷贝技术在文件拷贝时具有明显的优势。
实验
下面我们来通过代码来进行实验,同样拷贝一个从idea官网下载下来的zip文件(768M),对比一下三种拷贝的效率:
private File file = new File("D:\\java开发\\ideaIU-2019.3.5.win.zip");
private String targetPath = "D:\\java_code\\filecopy\\";
1、传统的拷贝方式:平均时间在9秒左右
@Test
public void testInputstream() throws Exception {
FileInputStream fileInputStream = new FileInputStream(file);
File targetFile = new File(targetPath + "inputstream.zip");
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
byte[] bytes = new byte[1024];
StopWatch stopWatch = new StopWatch();
stopWatch.start();
while (fileInputStream.read(bytes) != -1) {
fileOutputStream.write(bytes);
}
stopWatch.stop();
System.err.println(stopWatch.toString());
}
2、使用BufferedOutputStream缓冲输出流拷贝方式:平均时间在2秒4左右,BufferedOutputStream默认会用一个8kb的数组作为缓冲区,这样就不用每次写入一个字节都去通过系统调用来刷盘,可以减少io,自然比第一种方式快,但是我试过调整这个缓冲区的大小,耗时区别不大
@Test
public void testBuffedInputstream() throws Exception {
FileInputStream fileInputStream = new FileInputStream(file);
File targetFile = new File(targetPath + "bufferedinputstream.zip");
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 8192 * 4);
byte[] bytes = new byte[1024];
StopWatch stopWatch = new StopWatch();
stopWatch.start();
while (fileInputStream.read(bytes) != -1) {
bufferedOutputStream.write(bytes);
}
bufferedOutputStream.flush();
stopWatch.stop();
System.err.println(stopWatch.toString());
}
3、MMAP方式:平均时间在2秒左右
@Test
public void testMmap() throws Exception {
FileChannel sourceChannel = new RandomAccessFile(file, "r").getChannel();
File targetFile = new File(targetPath + "mmap.zip");
FileChannel targetChannel = new RandomAccessFile(targetFile, "rw").getChannel();
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
long size = sourceChannel.size();
// 创建源文件和目标文件的 MappedByteBuffer
MappedByteBuffer sourceBuffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
MappedByteBuffer targetBuffer = targetChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
// 使用 MappedByteBuffer 拷贝文件
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (long i = 0; i < size; i++) {
byte b = sourceBuffer.get();
targetBuffer.put(b);
}
stopWatch.stop();
System.err.println(stopWatch.toString());
}
4、DMA方式(这个不确定是不是用的DMA,因为是硬件层面的技术,无法直接验证,取决于操作系统和硬件):平均时间在1秒左右
@Test
public void testDma() throws Exception {
File targetFile = new File(targetPath + "dma.zip");
FileChannel sourceChannel = new FileInputStream(file).getChannel();
FileChannel targetChannel = new FileOutputStream(targetFile).getChannel();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 使用transferFrom或transferTo方法拷贝文件
// targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
// 或者
sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
stopWatch.stop();
System.err.println(stopWatch.toString());
}
总结:
1、零拷贝技术明显比传统的文件拷贝要快的多,而基于硬件层面实现的DMA要比软件层面的MMAP更快
2、适用场景:不需要cpu介入做运算的,比如mq消息中间件,消费者在消费消息的时候,mq服务器只需要把消息读出来发送给消费者,而不需要做运算,比较有名的kafka就使用了DMA,而rocketmq就使用了MMAP