零拷贝
在操作系统中进行的拷贝(如图二和图三),叫做CPU拷贝。
连接磁盘或网卡等硬件的拷贝(如图一和图四),叫做DMA拷贝。
零拷贝的定义
1)Zero-copy, 就是在操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升
2)在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据
3)Netty 中的 Zero-copy 与 OS 的 Zero-copy 不太一样, Netty的 Zero-coyp 完全是在用户态(Java 层面)的, 它的 Zero-copy 的更多的是偏向于优化数据操作
1)传统的拷贝方式(4次)
Socket网络缓冲区,也属于操作系统的内核缓冲区
图一
2)mmap = memory mapping 内存映射
图二
3)sendfile (linux2.1内核支持)
图三
4)sendfile with scatter/gather copy(批量sendfile)
从单个文件的处理,上升到多个物理地址的处理,提高处理速度
5)splice (拼接,在linux2.6内核支持)
在操作系统内核缓冲区和Socket网络缓冲区之间建立管道,来减少拷贝次数
图四
实现
public class ZeroCopyTest {
public static void main(String[] args) throws Exception {
// copyByMMap("test.txt", "test_new.txt");
copyBySendFile("test.txt", "test_new.txt");
}
public static void copyByMMap(String sourceName, String destName) throws Exception {
File source = new File(sourceName);
File dest = new File(destName);
if (!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileChannel inChannel = fis.getChannel();
FileOutputStream fos = new FileOutputStream(dest);
FileChannel outChannel = fos.getChannel();
// ByteBuffer子类 对应于mmap内存映射的拷贝方式
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, source.length());
outChannel.write(buffer);
buffer.clear();
inChannel.close();
fis.close();
outChannel.close();
fos.close();
}
/**
* 效率更高
*
* @param sourceName
* @param destName
* @throws Exception
*/
public static void copyBySendFile(String sourceName, String destName) throws Exception {
File source = new File(sourceName);
File dest = new File(destName);
if (!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileChannel inChannel = fis.getChannel();
FileOutputStream fos = new FileOutputStream(dest);
FileChannel outChannel = fos.getChannel();
// 通过transferTo 直接从A通道搬运数据到B通道
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
fis.close();
outChannel.close();
fos.close();
}
}