Netty
作為一個優(yōu)秀網絡框架壳嚎,其高效的內存操作也是使其變得高性能的很重要原因之一娄周。
眾所周知绎橘,Java
的NIO
中提供了類ByteBuffer
作為字節(jié)的容器迟赃,但是操作非常的復雜诈闺,Netty針對ByteBuffer
設計了一個替代類ByteBuf
渴庆,方便開發(fā)者操作字節(jié)。
ByteBuf API
對于任意一個ByteBuf
對象雅镊,都擁有三個非常重要的屬性:
- readerIndex:讀索引
- writerIndex:寫索引
- capacity:對象容量
ByteBuf
對象每讀取一個byte的數(shù)據襟雷,readerIndex就會往前推進,直到readerIndex到達capacity的值仁烹,所有的數(shù)據的數(shù)據都被讀取完耸弄,ByteBuf
不可再被讀取∽跨郑可以通過readableBytes()
方法獲取readerIndex
的值计呈。
相同地砰诵,writerIndex記錄了ByteBuf
對象使用了多少數(shù)據,可以通過writableBytes()
方法獲取writerIndex的值捌显。每當ByteBuf
被寫入了多少數(shù)據茁彭,writerIndex就會往前推進,直到值到達capacity的值扶歪,ByteBuf
會自動對空間進行擴容理肺。
對于任意一個ByteBuf
對象,我們都可以根據它的索引通過getByte()
方法隨機訪問中間的數(shù)據击罪。隨機訪問不會改變readerIndex
的值哲嘲。
通過array()
方法可以直接獲取,ByteBuf
中的Byte數(shù)組信息媳禁。
幾種ByteBuf模式
Netty的“Zero-Copy”設計非常出名,這主要就是依賴了Netty中ByteBuf
的設計画切。ByteBuf
主要有以下幾種模式:
1.Heap Buffer模式
顧名思義竣稽,這個模式下的字節(jié)是在Jvm的堆區(qū)操作的,也是最常見的內存操作了霍弹。
2.Direct Buffer模式
在JDK1.4中毫别,Java引入了一種直接內存,NIO可以通過本地方法
分配一些堆外的直接內存典格,這塊內存區(qū)不受Jvm的控制岛宦,理論上的無限的。
對于網絡Socket通信來說耍缴,這種內存區(qū)域的好處是Java在通信中砾肺,數(shù)據不必從Jvm中拷貝一份到系統(tǒng)的直接內存區(qū)上,操作系統(tǒng)的Socket接口可以直接處理這份在直接內存的數(shù)據防嗡。同時由于數(shù)據在堆外变汪,也避免了頻繁GC對這塊區(qū)域的影響。
ByteBuf
提供了Direct Buffer
模式蚁趁,我們可以直接通過ByteBuf
操作直接內存裙盾。
Direct Buffer
模式下,由于數(shù)據不在堆上面他嫡,ByteBuf
是不可以直接使用array()
方法獲取數(shù)據的番官。
3.Composite buffer模式
在TCP協(xié)議中,一份完整的數(shù)據總是被拆成好幾個包被發(fā)送或者接收钢属,一般情況下徘熔,程序會通過內存拷貝的方式將一組數(shù)據拷貝到一個大的數(shù)組中,形成一份完整的數(shù)據署咽。
而Composite buffer模式可以聚合多個ByteBuffer對象近顷,將這組數(shù)據的引用收集到一個ByteBuf
對象中生音,避免了數(shù)據的拷貝。
// 初始化一個Composite buffer模式的`ByteBuf`
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(size);
// 添加byteBuf對象
compositeByteBuf.addComponent(byteBuf1);
compositeByteBuf.addComponent(byteBuf2);
compositeByteBuf.addComponent(byteBuf3);
// 操作compositeByteBuf
handle(compositeByteBuf);
分配內存的方式
當然為了避免Netty本身內存使用過度窒升,Netty內部對所有的內存做了池化缀遍。通過ByteBufAllocator
類,我們可以分配一塊被池化的內存饱须,從而減少分配和釋放內存的開銷域醇。
ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer();
ByteBuf buffer = ByteBufAllocator.DEFAULT.ioBuffer();
ByteBuf buffer = ByteBufAllocator.DEFAULT.directBuffer();
ByteBuf buffer = ByteBufAllocator.DEFAULT.compositeBuffer();
如果我們希望使用一塊新的內存,或者對一個已經存在的內存進行包裝蓉媳,那么我們可以使用Unpooled
類來分配內存:
ByteBuf heapBuffer = buffer(128);
ByteBuf directBuffer = directBuffer(256);
ByteBuf wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);
ByteBuf copiedBuffe r = copiedBuffer(ByteBuffer.allocate(128));