Netty ByteBuf 幾種類型
1. 前言
上一節(jié),我們主要學(xué)習(xí)了 ByteBuf 的核心 API,相信大家都能掌握,本節(jié)主要介紹 ByteBuf 的幾種分類。
2. 創(chuàng)建一個 ByteBuf
常見創(chuàng)建 ByteBuf 主要有兩種方式,分別如下所示:
方式一:
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
方式二:
ByteBuf byteBuf = Unpooled.buffer(10);
思考:那么這兩種方式有什么關(guān)聯(lián)呢?
Unpooled.buffer
源碼,以下代碼是經(jīng)過整理,只保留了核心代碼。
public final class Unpooled {
private static final ByteBufAllocator ALLOC;
static {
ALLOC = UnpooledByteBufAllocator.DEFAULT;
}
public static ByteBuf buffer(int initialCapacity) {
return ALLOC.heapBuffer(initialCapacity);
}
public static ByteBuf directBuffer() {
return ALLOC.directBuffer();
}
}
通過源碼,我們可以知道,其實 Unpooled 工具類也是調(diào)用 ByteBufAllocator 去創(chuàng)建 ByteBuf 的。從字面上我們能夠大概猜到它針對的是非池化的 ByteBuf 進行創(chuàng)建的。
3. ByteBuf 分類
ByteBuf 是一個字節(jié)容器,底層是根據(jù)容量值來申請一塊內(nèi)存區(qū)域來存儲字節(jié)數(shù)組的。既然涉及到內(nèi)存,那么會分為直接內(nèi)存和 JVM 內(nèi)存,這個和 NIO 的直接緩沖器和非直接緩沖器是一樣的道理。直接內(nèi)存,速度很快,垃圾回收是不受 JVM 控制,容易造成內(nèi)存爆滿。
ByteBuf 主要分為三種類型
- Pooled 和 Unpooled,池化和非池化;
- Heap 和 Direct,堆內(nèi)存和直接內(nèi)存;
- Safe 和 Unsafe,安全和非安全。
池化和非池化: 池化就是用完就放回池子里面,比如我們所熟悉的數(shù)據(jù)庫連接池。非池化就是每次使用都重新創(chuàng)建,使用完成則立馬銷毀。從性能的角度來說,池化會比非池化相對高,因為可以重復(fù)利用,避免每次都重新創(chuàng)建。
堆內(nèi)存和直接內(nèi)存: 堆內(nèi)存是 JVM 內(nèi)部開辟的一塊內(nèi)存空間,它的生命周期受到 JVM 來管理,不容易造成內(nèi)存溢出的情況。直接內(nèi)存則是直接受操作系統(tǒng)管理了,如果數(shù)據(jù)量很大的情況,容易造成內(nèi)存溢出情況。
安全和非安全: 主要是 Java 操作底層操作數(shù)據(jù)的一種安全和非安全的方式。
根據(jù)不同類型進行組合,得到常見 ByteBuf 的實現(xiàn)類
- 池化 + 堆內(nèi)存,PooledHeapByteBuf;
- 池化 + 直接內(nèi)存,PooledDirectByteBuf;
- 池化 + 堆內(nèi)存 + 不安全,PooledUnsafeHeapByteBuf;
- 池化 + 直接內(nèi)存 + 不安全,PooledUnsafeDirectByteBuf;
- 非池化 + 堆內(nèi)存,UnpooledHeapByteBuf;
- 非池化 + 直接內(nèi)存,UnpooledDirectByteBuf;
- 非池化 + 堆內(nèi)存 + 不安全,UnpooledUnsafeHeapByteBuf;
- 非池化 + 直接內(nèi)存 + 不安全,UnpooledUnsafeDirectByteBuf。
4. ByteBufAllocator 的使用
由于 ByteBuf 的組合種類非常的多,如果讓用戶手工去創(chuàng)建的化,會非常的麻煩,并且對每種類型不熟悉,很容易出現(xiàn)性能問題。這點跟 Java 線程池有點類似,線程池的種類分好幾種,但是通常都是通過 Executors 工具類來進行線程池的創(chuàng)建。
其中,ByteBufAllocator 又主要分為兩種,分別是 UnpooledByteBufAllocator
和 PooledByteBufAllocator
。其實,一般情況下我們不需要直接使用具體的分配器,而是使用它默認的即可。
4.1 默認分配 - 池化 & 非池化
實例:
ByteBufAllocator byteBufAllocator = ByteBufAllocator.DEFAULT;
源碼:
public interface ByteBufAllocator {
ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;
}
源碼:以下源碼是經(jīng)過處理,只保留核心部分。
public final class ByteBufUtil {
static final ByteBufAllocator DEFAULT_ALLOCATOR;
static {
//1.分配類型
String allocType = SystemPropertyUtil.get("io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
//2.根據(jù)類型,創(chuàng)建不同的分配器
Object alloc;
if ("unpooled".equals(allocType)) {
alloc = UnpooledByteBufAllocator.DEFAULT;
} else if ("pooled".equals(allocType)) {
alloc = PooledByteBufAllocator.DEFAULT;
} else {
alloc = PooledByteBufAllocator.DEFAULT;
}
DEFAULT_ALLOCATOR = (ByteBufAllocator)alloc;
}
}
根據(jù)以上的源碼,我們可以知道,使用 ByteBufAlloctor 來創(chuàng)建 ByteBuf 時,會判斷使用池化還是非池化的分配器。
4.2 默認分配 - 堆內(nèi)存 & 直接內(nèi)存
實例:
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
源碼:以下源碼是經(jīng)過處理,只保留核心部分。
public abstract class AbstractByteBufAllocator implements ByteBufAllocator {
private final boolean directByDefault;
//構(gòu)造函數(shù)
protected AbstractByteBufAllocator(boolean preferDirect) {
this.directByDefault = preferDirect && PlatformDependent.hasUnsafe();
}
public ByteBuf buffer(int initialCapacity) {
return this.directByDefault ? this.directBuffer(initialCapacity) : this.heapBuffer(initialCapacity);
}
}
通過 directByDefault 來判斷是否選擇創(chuàng)建堆內(nèi)存還是直接內(nèi)存的 ByteBuf,而 directByDefault 是在構(gòu)造函數(shù)里面進行傳值的,那么它是一個抽象類,因此肯定是從其子類的構(gòu)造函數(shù)傳值進來。
繼續(xù)查看源碼:
public class PooledByteBufAllocator extends AbstractByteBufAllocator {
public PooledByteBufAllocator() {
//傳遞的是false
this(false);
}
}
public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {
public UnpooledByteBufAllocator(boolean preferDirect) {
this(preferDirect, false);
}
public UnpooledByteBufAllocator(boolean preferDirect, boolean disableLeakDetector) {
//傳遞值給父類的構(gòu)造函數(shù)
super(preferDirect);
this.disableLeakDetector = disableLeakDetector;
}
}
總結(jié),ByteBufAllocator 的核心兩個步驟分別如下:
- 確定是使用哪個分配器,池化還是非池化?
UnpooledByteBufAllocator
和PooledByteBufAllocator
; - 再確定是堆內(nèi)存還是直接內(nèi)存,主要是在
UnpooledByteBufAllocator
和PooledByteBufAllocator
的構(gòu)造函數(shù)里面?zhèn)髦荡_定。
4.3 核心方法
方式 | 描述 |
---|---|
buffer(); | 創(chuàng)建 ByteBuf(堆內(nèi)存還是直接內(nèi)存?),默認容量值 |
buffer(int var1); | 創(chuàng)建 ByteBuf(堆內(nèi)存還是直接內(nèi)存?),手工指定容量值 |
buffer(int var1, int var2); | 創(chuàng)建 ByteBuf(堆內(nèi)存還是直接內(nèi)存?),手工指定容量值和最大容量值 |
heapBuffer(); | 創(chuàng)建一個堆內(nèi)存的 ByteBuf,默認容量值 |
heapBuffer(int var1); | 創(chuàng)建一個堆內(nèi)存的 ByteBuf,手工指定容量值 |
heapBuffer(int var1, int var2); | 創(chuàng)建一個堆內(nèi)存的 ByteBuf,手工指定容量值和最大容量值 |
directBuffer(); | 創(chuàng)建一個直接內(nèi)存的 ByteBuf,默認容量值 |
directBuffer(int var1); | 創(chuàng)建一個直接內(nèi)存的 ByteBuf,手工指定容量值 |
directBuffer(int var1, int var2); | 創(chuàng)建一個直接內(nèi)存的 ByteBuf,手工指定容量值和最大容量值 |
一般推薦使用 buffer ()、buffer(int var1)
、buffer(int var1,int var2)
,因為 Netty 底層回去幫選擇創(chuàng)建最優(yōu)的 ByteBuf。
5. Unpooled 的使用
Unpooled 主要是使用了非池化技術(shù),可以創(chuàng)建堆內(nèi)存和直接內(nèi)存的 ByteBuf。
核心 API 如下所示:
方法 | 描述 |
---|---|
ByteBuf buffer() | 創(chuàng)建非池化 + 堆內(nèi)存的 ByteBuf,默認容量大小 |
ByteBuf buffer(int initialCapacity) | 創(chuàng)建非池化 + 堆內(nèi)存的 ByteBuf,并且可以指定容量大小 |
ByteBuf directBuffer() | 創(chuàng)建非池化 + 直接內(nèi)存的 ByteBuf,默認容量大小 |
directBuffer(int initialCapacity) | 創(chuàng)建非池化 + 直接內(nèi)存的 ByteBuf,并且可以指定容量大小 |
ByteBuf copiedBuffer(byte[] array) | 創(chuàng)建非池化 + 堆內(nèi)存的 ByteBuf,并且初始化字節(jié)數(shù)組 |
ByteBuf copiedBuffer(byte[] array, int offset, int length) | 創(chuàng)建非池化 + 堆內(nèi)存的 ByteBuf,并且把字節(jié)數(shù)組的部分內(nèi)容 初始化到 ByteBuf |
ByteBuf copiedBuffer(ByteBuf buffer) | 創(chuàng)建非池化 + 堆內(nèi)存的 ByteBuf,并且把參數(shù)的 ByteBuf 寫入到新創(chuàng)建的 ByteBuf 里 |
以上的方法是平時我們使用 Unpooled 時使用最多的,難度不大,只需要分清每個方法的作用是什么即可,可以根據(jù)自己需求選擇合適的 ByteBuf 類型。
6. 小結(jié)
本節(jié)主要介紹了 ByteBuf 的幾種核心類型以及創(chuàng)建 ByteBuf 的幾種方式
- 掌握 ByteBuf 的三種類型,分別是池化與非池化、堆內(nèi)存與直接內(nèi)存、安全與不安全,以及它們之間的含義;
- ByteBufAllocator 分配器,它是如何去創(chuàng)建 ByteBuf 的,幾種模式;
- Unpooled,Netty 提供的非池化工具類。