Netty基于不同的使用場(chǎng)景,提供了幾個(gè)ByteBuf當(dāng)中零拷貝的方法。這些方法和我們?cè)贜IO當(dāng)中談到的不同领虹,在NIO當(dāng)中的零拷貝最終是為了減少用戶態(tài)和內(nèi)核態(tài)之間的數(shù)據(jù)拷貝。
本章節(jié)我們針對(duì)Netty提供的幾個(gè)方法進(jìn)行簡(jiǎn)單學(xué)習(xí)和使用求豫。
一塌衰、零拷貝
先聲明一點(diǎn),如果要使用以下方法的話蝠嘉,可能要配合前面文章介紹的retain()方法去增加引用計(jì)數(shù)最疆,否則原ByteBuf被release()后,則會(huì)導(dǎo)致我們拷貝出來(lái)的buf使用失敗蚤告,導(dǎo)致異常努酸。
1.1 slice
“零拷貝”的體現(xiàn)之一,對(duì)原始 ByteBuf 進(jìn)行切片杜恰,切分成多個(gè) ByteBuf蚊逢,切片后的 ByteBuf 并沒(méi)有發(fā)生內(nèi)存復(fù)制,只是多了引用到分片后的Bytebuf箫章,然而還是使用的原始 ByteBuf 的內(nèi)存烙荷,切片后的 ByteBuf 維護(hù)獨(dú)立的 read,write 指針檬寂,修改子分片终抽,會(huì)修改原ByteBuf。
示例代碼:
public static void main(String[] args) {
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
byteBuf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,0});
printBuf(byteBuf);
//分片1
ByteBuf slice1 = byteBuf.slice(0, 5);
printBuf(slice1);
//分片2
ByteBuf slice2 = byteBuf.slice(5, 5);
printBuf(slice2);
//將最后一位0修改成10
slice2.setByte(4,10);
printBuf(slice2);
//打印修改后的byteBuf
printBuf(byteBuf);
}
static void printBuf(ByteBuf byteBuf){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i< byteBuf.writerIndex();i++) {
stringBuilder.append(byteBuf.getByte(i));
}
System.out.println(stringBuilder);
}
結(jié)果:
1234567890
12345
67890
678910
12345678910
注意:slice后的分片桶至,不能再次寫入新的數(shù)據(jù)昼伴,這回影響原ByteBuf。
1.2镣屹、duplicate
“零拷貝”的體現(xiàn)之一圃郊,拷貝了原始 ByteBuf 所有內(nèi)容,長(zhǎng)度仍然以byteBuf為準(zhǔn)女蜈,不能寫入新數(shù)據(jù)持舆,也是與原始 ByteBuf 使用同一塊底層內(nèi)存,只是讀寫指針是獨(dú)立的伪窖。
使用示例:
public class DuplicateTest {
public static void main(String[] args) {
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
byteBuf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,0});
//拷貝一塊buf
ByteBuf duplicate = byteBuf.duplicate();
printBuf(duplicate);
//將最后一位0修改成10逸寓,看一下byteBuf
duplicate.setByte(9,10);
printBuf(byteBuf);
// 寫入新數(shù)據(jù)11,看byteBuf
duplicate.writeByte(11);
printBuf(byteBuf);
}
static void printBuf(ByteBuf byteBuf){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i< byteBuf.writerIndex();i++) {
stringBuilder.append(byteBuf.getByte(i));
}
System.out.println(stringBuilder);
}
}
1.3 CompositeByteBuf
“零拷貝”的體現(xiàn)之一覆山,可以將多個(gè) ByteBuf 合并為一個(gè)邏輯上的 ByteBuf竹伸,避免拷貝。
public class CompositeByteBufTest {
public static void main(String[] args) {
ByteBuf byteBuf1 = ByteBufAllocator.DEFAULT.buffer(5);
byteBuf1.writeBytes(new byte[]{1, 2, 3, 4, 5});
ByteBuf byteBuf2 = ByteBufAllocator.DEFAULT.buffer(5);
byteBuf2.writeBytes(new byte[]{6, 7, 8, 9, 0});
CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();
// 組合兩個(gè)byteBuf簇宽,主要要使用帶有increaseWriteIndex的勋篓,否則會(huì)失敗吧享。
compositeByteBuf.addComponents(true,byteBuf1, byteBuf2);
printBuf(compositeByteBuf);
}
static void printBuf(ByteBuf byteBuf){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i< byteBuf.writerIndex();i++) {
stringBuilder.append(byteBuf.getByte(i));
}
System.out.println(stringBuilder);
}
}
1.4 Unpooled
Unpooled 是一個(gè)工具類,提供了非池化的 ByteBuf 創(chuàng)建譬嚣、組合耙蔑、復(fù)制等操作。
這里僅介紹其跟“零拷貝”相關(guān)的 wrappedBuffer 方法
使用示例:
public static void main(String[] args) {
ByteBuf byteBuf1 = ByteBufAllocator.DEFAULT.buffer(5);
byteBuf1.writeBytes(new byte[]{1, 2, 3, 4, 5});
ByteBuf byteBuf2 = ByteBufAllocator.DEFAULT.buffer(5);
byteBuf2.writeBytes(new byte[]{6, 7, 8, 9, 0});
// CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();
// // 組合兩個(gè)byteBuf孤荣,主要要使用帶有increaseWriteIndex的甸陌,否則會(huì)失敗。
// compositeByteBuf.addComponents(true,byteBuf1, byteBuf2);
// 組合兩個(gè)byteBuf盐股,底層使用CompositeByteBuf钱豁。
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(byteBuf1, byteBuf2);
printBuf(wrappedBuffer);
}
static void printBuf(ByteBuf byteBuf){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i< byteBuf.writerIndex();i++) {
stringBuilder.append(byteBuf.getByte(i));
}
System.out.println(stringBuilder);
}
二、深度拷貝
ByteBuf提供了copy方法疯汁,這一類方法是真正的拷貝原ByteBuf到新的內(nèi)存牲尺,返回一個(gè)新的ByteBuf,與原ByteBuf沒(méi)有關(guān)系幌蚊。
提供兩個(gè)拷貝谤碳,一個(gè)是全量;一個(gè)指定位置和長(zhǎng)度溢豆。
public abstract ByteBuf copy();
public abstract ByteBuf copy(int index, int length);
使用示例:
public class CopyTest {
public static void main(String[] args) {
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
byteBuf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,0});
ByteBuf copy1 = byteBuf.copy();
printBuf(copy1);
ByteBuf copy2 = byteBuf.copy(5, 5);
printBuf(copy2);
}
static void printBuf(ByteBuf byteBuf){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i< byteBuf.writerIndex();i++) {
stringBuilder.append(byteBuf.getByte(i));
}
System.out.println(stringBuilder);
}
}
關(guān)于Bytebuf的入門就介紹這么多了蜒简,后面會(huì)深入去探討更細(xì)節(jié)的內(nèi)容。
Bytebuf簡(jiǎn)單總結(jié):
- 池化 - 可以重用池中 ByteBuf 實(shí)例漩仙,更節(jié)約內(nèi)存搓茬,減少內(nèi)存溢出的可能
- 讀寫指針?lè)蛛x,不需要像 ByteBuffer 一樣切換讀寫模式
- 可以自動(dòng)擴(kuò)容
- 支持鏈?zhǔn)秸{(diào)用队他,使用更流暢
- 很多地方體現(xiàn)零拷貝卷仑,例如 slice、duplicate麸折、CompositeByteBuf