簡介
指計算機在執(zhí)行操作的時候,cpu不需要先將數據從某處復制到一個特定地方,節(jié)省cpu的時鐘周期和內存帶寬
常規(guī)流程

DMA:DIRECT MEMORY ACCESS
這個東西不消耗cpu(從磁盤到內核read緩沖區(qū),從內核socket緩沖區(qū)到網卡都是DMA)
mmap

實現原理:所有的操作系統(tǒng)都使用的是虛擬內存,取代物理內存,虛擬內存遠大于物理內存,
所有一個以上的虛擬內存可以指向同一個物理內存,這樣DMA就可以填充對內核及用戶進程空間同時可見的緩沖區(qū)
節(jié)省了內核到用戶空間的copy,但是從內核read緩沖區(qū)到內核socket緩沖區(qū)的拷貝沒有節(jié)省
NIO的FileChannel.map底層就是封裝了 linux的 mmap
java實現
public static void main(String[] args) {
File file = new File("");
try {
//打開FileChannel只能讀取
FileChannel fileChannelIn = new FileInputStream(file).getChannel();
//打開FileChannel只能寫入
FileChannel fileChannel1Out = new FileInputStream("").getChannel();
//讀入數據轉為MappedByteBuffer
MappedByteBuffer mbb = fileChannelIn.map(FileChannel.MapMode.READ_ONLY,0,file.length());
//創(chuàng)建解碼器
Charset charset = Charset.forName("UTF-8");
//寫入數據
fileChannel1Out.write(mbb);
mbb.clear();
//創(chuàng)建解碼器
CharsetDecoder decoder = charset.newDecoder();
// 使用解碼器將byteBuffer轉為CharBuffer
CharBuffer charBuffer = decoder.decode(mbb);
System.out.println(charBuffer);
}catch (Exception e){
return;
}
}
sendFile
適用于:保存了mmap的不需要來回拷貝的優(yōu)點,適用于應用進程不需要對讀取數據做任何處理的場景,類似于一種完全意義上的數據傳輸
這個實現的拷貝,主要做的實現了數據的偏移量offset,數據長度length的拷貝

不存在用戶緩沖區(qū)(不需要使用)
數據甚至不用從內核緩沖區(qū)拷貝到socket的緩沖區(qū),只需要將內核緩沖區(qū)的拷貝一些offset和length到socket緩沖區(qū)
數量少可以忽略所以認為不存在此次copy
nio中使用的是FileChannel.transferTo,底層封裝的是linux的sendfile這個方法
只有兩次的上下文切換和兩次dma切換
public static void main(String[] args) {
String files[] = new String[1];
files[0] = System.getProperty("usr.dir") + "/c:\\aa\\dn.txt";
catFiles(Channels.newChannel(System.out), files);
}
private static void catFiles(WritableByteChannel target, String[] files) {
try{
for (int i = 0; i < files.length; i++) {
FileInputStream fileInputStream = new FileInputStream(files[1]);
FileChannel channel = fileInputStream.getChannel();
channel.transferTo(0,channel.size(),target);
channel.close();
fileInputStream.close();
}
} catch (Exception e){
System.out.println(e);
}
}
總結
傳統(tǒng)io有4次上下文切換,4次拷貝
磁盤 》 內核read 》用戶 》 內核socket 》網卡(協(xié)議引擎)
mmap:三次拷貝(兩次DMA + 一次內核read緩沖到內核socket緩存)。將磁盤文件映射到內存,支持讀寫內存文件直接反映到磁盤文件上,適合小文件讀取
sendfile:兩次拷貝(兩次DMA),適合大文件傳輸
Netty的零拷貝
1.Netty的接收和發(fā)送ByteBuffer采用的是Direct buffers,使用堆外內存直接進行socket讀取,如果是傳統(tǒng)內存,jvm會將堆內存buffer拷貝一份到直接內存中,再寫入socket,所以傳統(tǒng)堆內存在消息發(fā)送的過程中多了一次緩沖區(qū)的內存拷貝
2.提供了bytebuf對象,整合了bytebuffer,用戶通過bytebuf可以像操作一個buffer那樣對組合的buffer進行操作
做法是實現CompositeByteBuf

CompositeByteBuf 實際就是個 ByteBuf 的包裝器,它將多個 ByteBuf 組合成一個集合
該類相關定義如下
private static final ByteBuffer EMPTY_NIO_BUFFER = Unpooled.EMPTY_BUFFER.nioBuffer();
private static final Iterator<ByteBuf> EMPTY_ITERATOR = Collections.<ByteBuf>emptyList().iterator();
private final ByteBufAllocator alloc;
private final boolean direct;
private final List<Component> components; private final int maxNumComponents;
private boolean freed;
3.文件傳輸采用transferTo,可以將文件緩沖區(qū)的數據發(fā)送到目標channel避免通過傳統(tǒng)的write方式導致的內存拷貝問題
這個就是nio里的FileChannel的transferTo
4.wrap
5.slice