1 基本概念
在網絡編程中,字節數據的處理是核心環節之一。無論是客戶端與服務器之間的通信,還是數據的編解碼操作,都離不開對字節緩沖區的高效管理。Java 原生的?ByteBuffer
?雖然提供了基礎功能,但在靈活性、性能和易用性上存在諸多局限。而 Netty 框架提供的?ByteBuf
?則徹底解決了這些問題,成為處理網絡字節數據的首選工具。
2 為什么需要?Bytebuf
Java 原生的?ByteBuffer
?存在以下明顯短板:
- 單指針設計:僅有一個?
position
?指針,讀寫操作需頻繁調用?flip()
?切換模式,容易出錯; - 固定容量:創建后容量不可動態調整,超出容量時需手動擴容,操作繁瑣;
- 內存管理復雜:堆外內存(直接內存)的釋放依賴 GC,可能導致內存泄漏;
- API 設計不友好:缺乏直接讀寫基本類型、字符串的便捷方法。
相比之下,ByteBuf
?針對網絡編程場景進行了全面優化:
- 采用雙指針設計,讀寫無需切換模式;
- 支持自動擴容,無需手動處理容量不足問題;
- 內置引用計數機制,精準控制內存生命周期;
- 提供豐富的 API,簡化字節數據操作。
3?Bytebuf數據結構
?3.1 創建一個空Bytebuf
public static void main(String[] args) {// 空ByteBufemptyByteBuf();}private static void emptyByteBuf() {// byteBuf 分配一塊內存,自動判斷是否分配堆內存或者堆外內存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();printMsg(byteBuf);}private static void printMsg(ByteBuf byteBuf) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("read index:").append(byteBuf.readerIndex()).append("\n");stringBuilder.append("write index:").append(byteBuf.writerIndex()).append("\n");stringBuilder.append("capacity:").append(byteBuf.capacity()).append("\n");stringBuilder.append("maxCapacity:").append(byteBuf.maxCapacity()).append("\n");ByteBufUtil.appendPrettyHexDump(stringBuilder, byteBuf);System.out.println(stringBuilder.toString());}
輸出
read index:0
write index:0
capacity:256
maxCapacity:2147483647?
數據為空時bytebuf的內存結構
3.2 往Bytebuf中寫入數據?
private static void writeByte() {// byteBuf 分配一塊內存,自動判斷是否分配堆內存或者堆外內存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();// 寫入1個字節byteBuf.writeBytes(new byte[]{(byte)1});printMsg(byteBuf);}private static void printMsg(ByteBuf byteBuf) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("read index:").append(byteBuf.readerIndex()).append("\n");stringBuilder.append("write index:").append(byteBuf.writerIndex()).append("\n");stringBuilder.append("capacity:").append(byteBuf.capacity()).append("\n");stringBuilder.append("maxCapacity:").append(byteBuf.maxCapacity()).append("\n");ByteBufUtil.appendPrettyHexDump(stringBuilder, byteBuf);System.out.println(stringBuilder.toString());}
read index:0
write index:1
capacity:256
maxCapacity:2147483647
+-------------------------------------------------+
| ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?a ?b ?c ?d ?e ?f |
+--------+-------------------------------------------------+----------------+
|00000000| 01 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|. ? ? ? ? ? ? ? |
+--------+-------------------------------------------------+----------------+
?寫入1個字節內存結構
3.3?Bytebuf中讀取數據??
private static void readByte() {// byteBuf 分配一塊內存,自動判斷是否分配堆內存或者堆外內存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();// 寫入1個字節byteBuf.writeBytes(new byte[]{(byte)1, (byte)2});printMsg(byteBuf);// 讀取1個字節byte b = byteBuf.readByte();printMsg(byteBuf);}private static void printMsg(ByteBuf byteBuf) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("read index:").append(byteBuf.readerIndex()).append("\n");stringBuilder.append("write index:").append(byteBuf.writerIndex()).append("\n");stringBuilder.append("capacity:").append(byteBuf.capacity()).append("\n");stringBuilder.append("maxCapacity:").append(byteBuf.maxCapacity()).append("\n");ByteBufUtil.appendPrettyHexDump(stringBuilder, byteBuf);System.out.println(stringBuilder.toString());}
read index:0
write index:2
capacity:256
maxCapacity:2147483647
+-------------------------------------------------+
| ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?a ?b ?c ?d ?e ?f |
+--------+-------------------------------------------------+----------------+
|00000000| 01 02 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |.. ? ? ? ? ? ? ?|
+--------+-------------------------------------------------+----------------+
read index:1
write index:2
capacity:256
maxCapacity:2147483647
+-------------------------------------------------+
| ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?a ?b ?c ?d ?e ?f |
+--------+-------------------------------------------------+----------------+
|00000000| 02 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|. ? ? ? ? ? ? ? |
+--------+-------------------------------------------------+----------------+
?
?3.4 Bytebuf讀取后重復讀取
public static void main(String[] args) {// jvm堆內存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();// 寫數據byteBuf.maxWritableBytes();byteBuf.writeBytes(new byte[] {1, 2, 3, 4});printMsg(byteBuf);// 重置,寫指針復位到起始位置重新寫入,覆蓋寫byteBuf.resetWriterIndex();byteBuf.writeInt(6);printMsg(byteBuf);// 讀byteBuf.markReaderIndex();byte a = byteBuf.readByte();byte b = byteBuf.readByte();byteBuf.resetReaderIndex();printMsg(byteBuf);}private static void printMsg(ByteBuf byteBuf) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("read index:").append(byteBuf.readerIndex()).append("\n");stringBuilder.append("write index:").append(byteBuf.writerIndex()).append("\n");stringBuilder.append("capacity:").append(byteBuf.capacity()).append("\n");stringBuilder.append("maxCapacity:").append(byteBuf.maxCapacity()).append("\n");ByteBufUtil.appendPrettyHexDump(stringBuilder, byteBuf);System.out.println(stringBuilder.toString());}
read index:0
write index:4
capacity:256
maxCapacity:2147483647
+-------------------------------------------------+
| ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?a ?b ?c ?d ?e ?f |
+--------+-------------------------------------------------+----------------+
|00000000| 01 02 03 04 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |.... ? ? ? ? ? ?|
+--------+-------------------------------------------------+----------------+
read index:0
write index:8
capacity:256
maxCapacity:2147483647
+-------------------------------------------------+
| ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?a ?b ?c ?d ?e ?f |
+--------+-------------------------------------------------+----------------+
|00000000| 01 02 03 04 01 02 03 04 ? ? ? ? ? ? ? ? ? ? ? ? |........ ? ? ? ?|
+--------+-------------------------------------------------+----------------+
read index:0
write index:4
capacity:256
maxCapacity:2147483647
+-------------------------------------------------+
| ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?a ?b ?c ?d ?e ?f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 06 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |.... ? ? ? ? ? ?|
+--------+-------------------------------------------------+----------------+
?
?備注:markWriterIndex、resetWriterIndex一般是配套使用,如果未標記寫指針的位置默認從起始位置開始;markReaderIndex、resetReaderIndex一般也是配套使用,如果未標記讀取的位置,則從起始位置重新讀取
4 Bytebuf擴容
- 初始嘗試:如果?
minNewCapacity
?小于等于 4MB(4194304
?字節),新容量為大于等于?minNewCapacity
?的最小 2 的冪; - 超過 4MB 時:如果?
minNewCapacity
?大于 4MB,新容量為大于等于?minNewCapacity
?且為 4MB 整數倍的值; - 上限控制:新容量不能超過?
ByteBuf
?的最大容量。
案例: 未超過4M,成倍增長
public class ByteBufCapacity {public static void main(String[] args) {// jvm堆內存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();for (int i=0; i<256; i++) {byteBuf.writeBytes(new byte[] {(byte) i});}printMsg(byteBuf);for (int i=0; i<256; i++) {byteBuf.writeBytes(new byte[] {(byte) i});}printMsg(byteBuf);}private static void printMsg(ByteBuf byteBuf) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("read index:").append(byteBuf.readerIndex()).append("\n");stringBuilder.append("write index:").append(byteBuf.writerIndex()).append("\n");stringBuilder.append("capacity:").append(byteBuf.capacity()).append("\n");stringBuilder.append("maxCapacity:").append(byteBuf.maxCapacity()).append("\n");
// ByteBufUtil.appendPrettyHexDump(stringBuilder, byteBuf);System.out.println(stringBuilder.toString());}
read index:0
write index:256
capacity:256
maxCapacity:2147483647read index:0
write index:512
capacity:512
maxCapacity:2147483647
?大于4M,每次增加4M擴容
package com.bonnie.netty.bytebuf;import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;public class ByteBufCapacity {public static void main(String[] args) {// jvm堆內存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();// 4Mfor (int i=0; i<4194304; i++) {byteBuf.writeBytes(new byte[] {(byte) i});}printMsg(byteBuf);// 擴容+4Mfor (int i=0; i<4194304; i++) {byteBuf.writeBytes(new byte[] {(byte) i});}printMsg(byteBuf);// 擴容+4Mfor (int i=0; i<4194304; i++) {byteBuf.writeBytes(new byte[] {(byte) i});}printMsg(byteBuf);}private static void printMsg(ByteBuf byteBuf) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("read index:").append(byteBuf.readerIndex()).append("\n");stringBuilder.append("write index:").append(byteBuf.writerIndex()).append("\n");stringBuilder.append("capacity:").append(byteBuf.capacity()).append("\n");stringBuilder.append("maxCapacity:").append(byteBuf.maxCapacity()).append("\n");
// ByteBufUtil.appendPrettyHexDump(stringBuilder, byteBuf);System.out.println(stringBuilder.toString());}}
read index:0
write index:4194304
capacity:4194304
maxCapacity:2147483647read index:0
write index:8388608
capacity:8388608
maxCapacity:2147483647read index:0
write index:12582912
capacity:12582912
maxCapacity:2147483647
?5 堆內存和堆外內存
實堆內的意思就是java虛擬機里面的堆空間,而堆外的意思是java進程中系統 為它分配的堆空間,jvm堆中的數據如果想要寫入磁盤,就會進行write系統調用,調用過程為:jvm堆->系統 堆->PageCache->磁盤,如果數據是放在系統heap中,調用過程為:系統堆->PageCache->磁盤。 我們可以看到,使用堆內內存寫入數據會少一次的拷貝次數