Linux、Java、Netty的零拷貝

零拷貝的概念

在不同場景下,零拷貝的概念是不同的。
在操作系統層面,零拷貝是指在用戶態和內核態的拷貝次數爲0。
在Java中有一些類是支持零拷貝的(如果操作系統支持),對應的是操作系統的零拷貝。
在Netty中,指的只是在用戶層面(java層面)的拷貝次數爲0。

零拷貝如何實現

操作系統層面

在操作系統的層面實現零拷貝依賴於操作系統的命令,主要有兩種方案:1.sendfile命令。2.mmap命令。
這裏注意:在操作系統層面的零拷貝是有一定場景約束的,必須限制的把文件發送到socket或者從socket讀取到file,中間是不能對文件進行修改的。對應在sendfile或者mmap的命令上也可以提現出來,這兩個命令都含有file_fd(文件描述符)相關的參數。對應到Java nio也可以體現,就是FileChannel.transferToFileChannel.transferFrom都含有File相關參數。Java nio中MappedByteBuffer也是對應的零拷貝的內存,這個類也是對應的一個File。

Netty層面

在Netty層面,零拷貝就是減少Java層面內存的拷貝。比如:1.使用直接內存,就減少了直接內存與堆內存之間的拷貝。2.當多個ByteBuf組合成一個Bytebuf時,使用CompositeByteBuf組合而不是複製數據。3.與文件相關操作時使用Java nio的FileChannel.transferTo。
Netty的直接內存是什麼?
比如我們使用 ByteBuf byteBuf = Unpooled.directBuffer(10);申請一個直接內存。
Netty使用Unsafe.allocateMemory申請內存,返回內存的地址,用Unsafe.allocateMemory返回的地址和長度等包裝成DirectByteBuffer對象。這個DirectByteBuffer是Java nio包類的類。再使用DirectByteBuffer對象來構造ByteBuf對象。

總結

操作系統層面的零拷貝有場景限制,就是文件的發送與接收,中間不能修改數據。
如果我們從socket接收數據,進行計算以後發送出去,這個過程不涉及File。Netty只是在我們見的到的Java層面減少了數據的複製。

這裏還有一個問題:Netty裏使用的直接內存到底是什麼?在操作系統層面內存是分內核態和用戶態的,那Netty裏的直接內存是內核態呢還是用戶態?數據從客戶端發送過來,存儲在網卡中,通過DMA read到內核緩衝區,Netty使用的直接內存是內核緩衝區裏的數據嗎?還是需要一次內存拷貝到用戶態。

首先,Netty裏的直接內存是Java nio的DirectByteBuffer
從操作系統上講,內存分兩部分:內核態(由操作系統內核操作,讀寫磁盤,讀寫網絡都是由這負責);用戶態(我們的進程使用的內存。)
jvm啓動的時候會在用戶態申請一塊內存,申請的這塊內存中有一部分會被稱爲堆,一般我我們申請的對象就會放在這個堆上,堆上的對象是受gc管理的。那麼除了堆內的內存,其他的內存都被稱爲對外內存。在堆外內存中如果我們是通過Java的DirectByteBuffer申請的,那麼這塊內存其實也是間接受gc管理的,而如果我們通過jni直接調用c函數申請一塊堆外內存,那麼這塊內存就只能我們自己手動管理了。
當我們在Java中發起一個文件讀操作會發生什麼呢?首先內核會將數據從磁盤讀到內核態內存,再從內核態內存拷貝到用戶態的堆外內存(這部分是jvm實現),然後再將數據從堆外拷貝到堆內。拷貝到堆內其實就是我們在Java中自己手動申請的byte數組中。
而使用DirectByteBuffer就減少了從堆外內存到堆內內存的拷貝過程。

參考

第九周–零拷貝(高性能IO的關鍵之一)

Netty中的零拷貝

共享內存mmap介紹

Java直接內存是屬於內核態還是用戶態?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章