java 检查bytebuf长度_Java学习笔记16-Netty缓冲区ByteBuf详解
Java學習筆記16-Netty緩沖區ByteBuf詳解
Netty自己的ByteBuf
ByteBuf是為解決ByteBuffer的問題和滿足網絡應用程序開發人員的日常需求而設計的。
JDK ByteBuffer的缺點:
無法動態擴容:長度是固定的,不能動態擴展和收縮,當數據大于ByteBuffer容量時,會發生索引越界異常。
API使用復雜:讀寫的時候需要手工調用flip()和rewind()等方法,使用時需要非常謹慎的使用這些api,否則很容易出現錯誤。
ByteBuf做了哪些增強:
API操作便捷性
動態擴容
多種ByteBuf實現
高效的零拷貝機制
ByteBuf操作
ByteBuf三個重要屬性:readerIndex讀取位置、writerIndex寫入位置、capacity容量
提供了兩個指針變量來支持順序讀和寫操作,分別是readerIndex和writerIndex
常用方法定義
隨機訪問索引 getByte
順序讀 read*
順序寫 write*
清除已讀內容 discardReadBytes
清除緩沖區 clear
搜索操作
標記和重置
引用計數和釋放
discardable bytes
readable bytes
writable bytes
已讀可丟棄區域
可讀區域
待寫區域
0<= readerIndex
<= writerIndex
<= capacity
ByteBuf動態擴容
capacity默認值:256字節;最大值:Integer.MAX_VALUE(2GB)
write*方法調用時,通過AbstractByteBuf.ensureWritable0進行檢查。
容量計算方法:AbstractByteBufAllocator.calculateNewCapacity(新capacity的最小要求,capacity最大值)
根據新capacity的最小值要求,對應有兩套計算方法:
沒超過4M:從64字節開始,每次增加1倍,直至計算出來的newCapacity滿足新容量的最小要求。
示例:當前大小256,已寫250,繼續寫10字節數據,需要的容量最小要求是261,則新容量是64*2*2*2=512
超過4M:新容量 = 新容量最小要求 / 4M * 4M +4M
示例:當前大小3M,已寫3M,繼續寫2M數據,需要的容量最小要求是5M,則新容量是9M(不能超過最大值)。
4M的來源:一個固定的閥值AbstractByteBufAllocator.CALCULATE_THRESHOLD
選擇合適的ByteBuf實現
了解核心的:3個緯度的劃分方式,8種具體實現
堆內/堆外
是否池化
訪問方式
具體實現類
備注
unpool
safe
UnpooledHeapByteBuf
數組實現
heap堆內
unsafe
UnpooledUnsafeHeapByteBuf
Unsafe類直接操作內存
pool
safe
PooledHeapByteBuf
~
unsafe
PooledUnsafeHeapByteBuf
~
unpool
safe
UnpooledDirectByteBuf
NIO DirectByteBuffer
direct堆外
unsafe
UnpooledUnsafeDirectByteBuf
~
pool
safe
PooledDirectByteBuf
~
unsafe
PooledUnsafeDirectByteBuf
~
在使用中,都是通過ByteBufAllocator分配器進行申請,同時分配器具備有內存管理的功能
Unsafe的實現
unsafe意味著不安全的操作。但是更底層的操作會帶來性能的提升和特殊功能,Netty中會盡力使用unsafe。
Java語言很重要的特性是“一次編寫到處運行”,所以它針對底層的內存或者其他操作,做了很多封裝。
而unsafe提供了一系列我們操作底層的方法,可能會導致不兼容或者不可知的異常。
Info.僅返回一些低級的內存信息
Objects.提供用于操作對象及其字段的方法
Classes.提供用于操作類及其靜態字段的方法
addressSize
allocateInstance
staticFieldOffset
pageSize
objectFieldOffset
defineClass
defineAnonymousClass
ensureClassInitialized
Synchronization.低級的同步原語
Memory.直接訪問內存方法
Arrays.操作數組
monitorEnter
allocateMemory
arrayBaseOffset
tryMonitorEnter
copyMemory
arrayIndexScale
monitorExit
freeMemory
compareAndSwapInt
getAddress
putOrderedInt
getInt
putInt
PooledByteBuf對象、內存復用
PoolThreadCache:PooledByteBufAllocator實例維護的一個線程變量。
多種分類的MemoryRegionCache數組用作內存緩存,MemoryRegionCache內部是鏈表,隊列里面存Chunk。
PoolChunk里面維護了內存引用,內存復用的做法就是把buf的memory指向chunk的memory。
PooledByteBufAllocator.ioBuffer運作過程梳理:
EventLoop - Thread --allocate--> Arena(負責buf分配管理) -->
創建或復用ByteBuf對象
PooledByteBuf
stack
RECYCLER ---->
buffer
cache
嘗試從對應的緩存 復用內存空間
PoolThreadCache
TINY_MR_CACHE * 32 Q[512]
SMALL_MR_CACHE * 4 Q[256]
NORMAL_MR_CACHE * 3 Q[64]
無緩存時,從內存中申請 直接向內存申請 unpool
零拷貝機制
Netty的零拷貝機制,是一種應用層的實現。和底層JVM、操作系統內存機制并無過多關聯。
CompositeByteBuf,將多個ByteBuf合并為一個邏輯上的ByteBuf,避免了各個ByteBuf之間的拷貝。
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
ByteBuf newBuffer = compositeByteBuf.addComponents(true, buffer1, buffer2);
wrappedBuffer()方法,將byte[]數組包裝成ByteBuf對象。
ByteBuf newBuffer = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4, 5});
slice()方法。將一個ByteBuf對象切分成多個ByteBuf對象。
ByteBuf buffer1 = Unpooled.wrappedBuffer("hello".getBytes());
ByteBuf newBuffer = buffer1.slice(1, 2);
ByteBuf測試代碼
import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import java.util.Arrays;
/**
* @Author: Wenx
* @Description:
* @Date: Created in 2019/11/25 22:31
* @Modified By:
*/
public class ByteBufDemo {
public static void main(String[] args) {
apiTest();
compositeTest();
wrapTest();
sliceTest();
}
public static void apiTest() {
// +-------------------+------------------+------------------+
// | discardable bytes | readable bytes | writable bytes |
// | | (CONTENT) | |
// +-------------------+------------------+------------------+
// | | | |
// 0 <= readerIndex <= writerIndex <= capacity
// 1.創建一個非池化的ByteBuf,大小為10個字節
ByteBuf buf = Unpooled.buffer(10);
//ByteBuf buf = Unpooled.directBuffer(10);
println("1.原始ByteBuf為", buf);
// 2.寫入一段內容
byte[] bytes = {1, 2, 3, 4, 5};
buf.writeBytes(bytes);
print("2.寫入的bytes為", bytes);
println("寫入內容后ByteBuf為", buf);
// 3.讀取一段內容
byte b1 = buf.readByte();
byte b2 = buf.readByte();
print("3.讀取的bytes為", new byte[]{b1, b2});
println("讀取內容后ByteBuf為", buf);
// 4.將讀取的內容丟棄
buf.discardReadBytes();
println("4.將讀取的內容丟棄后ByteBuf為", buf);
// 5.清空讀寫指針
buf.clear();
println("5.清空讀寫指針后ByteBuf為", buf);
// 6.再次寫入一段內容,比第一段內容少
byte[] bytes2 = {1, 2, 3};
buf.writeBytes(bytes2);
print("6.寫入的bytes為", bytes2);
println("寫入內容后ByteBuf為", buf);
// 7.將ByteBuf清零
buf.setZero(0, buf.capacity());
println("7.將內容清零后ByteBuf為", buf);
// 8.再次寫入一段超過容量的內容
byte[] bytes3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
buf.writeBytes(bytes3);
print("8.寫入的bytes為", bytes3);
println("寫入內容后ByteBuf為", buf);
// 隨機訪問索引 getByte
// 順序讀 read*
// 順序寫 write*
// 清除已讀內容 discardReadBytes
// 清除緩沖區 clear
// 搜索操作
// 標記和重置
// 完整代碼示例:參考
// 搜索操作 讀取指定位置 buf.getByte(1);
}
public static void compositeTest() {
ByteBuf buffer1 = Unpooled.buffer(3);
buffer1.writeByte(1);
ByteBuf buffer2 = Unpooled.buffer(3);
buffer2.writeByte(4);
print("buffer1為", buffer1);
print("buffer2為", buffer2);
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
ByteBuf newBuffer = compositeByteBuf.addComponents(true, buffer1, buffer2);
println("CompositeByteBuf為", newBuffer);
}
public static void wrapTest() {
byte[] arr = {1, 2, 3, 4, 5};
ByteBuf newBuffer = Unpooled.wrappedBuffer(arr);
print("byte[]為", arr);
print("wrappedBuffer為", newBuffer);
print("newBuffer.getByte(4)為", newBuffer.getByte(4));
arr[4] = 6;
println("byte[4] = 6; 后newBuffer.getByte(4)為", newBuffer.getByte(4));
}
public static void sliceTest() {
ByteBuf oldBuffer = Unpooled.wrappedBuffer("hello".getBytes());
ByteBuf newBuffer = oldBuffer.slice(1, 2);
print("oldBuffer為", oldBuffer);
print("oldBuffer.slice(1, 2); 為", newBuffer);
print("newBuffer.getByte(0)為", newBuffer.getByte(0));
print("newBuffer.getByte(1)為", newBuffer.getByte(1));
// 新buf中原buf的引用
ByteBuf buf = newBuffer.unwrap();
print("newBuffer.unwrap()為", buf);
print("buf.getByte(0)為", buf.getByte(0));
print("buf.getByte(1)為", buf.getByte(1));
print("buf.getByte(2)為", buf.getByte(2));
print("buf.getByte(3)為", buf.getByte(3));
print("buf.getByte(4)為", buf.getByte(4));
}
private static void print(String str, byte b) {
System.out.println(String.format("%s==========>%s", str, b));
}
private static void print(String str, byte[] bytes) {
System.out.println(String.format("%s==========>%s", str, Arrays.toString(bytes)));
}
private static void print(String str, ByteBuf buf) {
print(str, buf, "");
}
private static void print(String before, ByteBuf buf, String after) {
byte[] bytes;
if (buf.hasArray()) {
bytes = buf.array();
} else {
int capacity = buf.capacity();
bytes = new byte[capacity];
for (int i = 0; i < buf.capacity(); i++) {
bytes[i] = buf.getByte(i);
}
}
System.out.println(String.format("%s==========>%s(ridx:%s, widx: %s, cap: %s)%s", before, Arrays.toString(bytes), buf.readerIndex(), buf.writerIndex(), buf.capacity(), after));
}
private static void println(String str, byte b) {
System.out.println(String.format("%s==========>%s
", str, b));
}
private static void println(String str, ByteBuf buf) {
print(str, buf, "
");
}
}
總結
以上是生活随笔為你收集整理的java 检查bytebuf长度_Java学习笔记16-Netty缓冲区ByteBuf详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 热部署在java中的包名_09-spri
- 下一篇: ajax 获取java数据_如何使用Aj