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详解相关推荐

  1. 小猫爪:i.MX RT1050学习笔记26-RT1xxx系列的FlexCAN详解

    i.MX RT1050学习笔记26-RT1xxx系列的FlexCAN详解 1 前言 2 FlexCAN简介 2.1 MB(邮箱)系统 2.1.1 正常模式下 2.1.2 激活了CAN FD情况下 2. ...

  2. IP地址和子网划分学习笔记之《IP地址详解》

    在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. IP地址和子网划分学习笔记相关篇章: 1.I ...

  3. IP地址和子网划分学习笔记之《子网划分详解》

    一,子网划分概述 IP地址和子网划分学习笔记相关篇章: 1.IP地址和子网划分学习笔记之<预备知识:进制计数> 2.IP地址和子网划分学习笔记之<IP地址详解> 3.IP地址和 ...

  4. JDBC学习笔记02【ResultSet类详解、JDBC登录案例练习、PreparedStatement类详解】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  5. 我的学习笔记——CSS背景渐变(Gradients)详解

    我的学习笔记--CSS背景渐变(Gradients)详解 一.线性渐变(Linear Gradients) 1.语法 background-image: linear-gradient(directi ...

  6. java基本语法心得_Java学习笔记(一)——基础语法(上)

    Java学习笔记(一)--基础语法(上) 软件构造 写在前面 编写Java程序时,应注意以下几点:大小写敏感:Java是大小写敏感的,这就意味着标识符Hello与hello是不同的. 类名:对于所有的 ...

  7. redis学习笔记(2)之redis主从详解

    redis主从详解 主从详解 主从配置 拓扑 原理 数据同步 概念 复制偏移量 复制积压缓冲区 主节点运行ID Psync命令 全量复制流程 部分复制流程 心跳 缓冲大小调节 读写分离 内容来源为六星 ...

  8. Apollo星火计划学习笔记——Apollo决策规划技术详解及实现(以交通灯场景检测为例)

    文章目录 前言 1. Apollo决策技术详解 1.1 Planing模块运行机制 1.2 Apollo决策功能的设计与实现 1.2.1参考路径 Reference Line 1.2.2 交规决策 T ...

  9. 学习笔记——Maven pom.xml配置详解

    POM的全称是"ProjectObjectModel(项目对象模型)". pom.xml详解 声明规范 <projectxmlns="http://maven.ap ...

最新文章

  1. 深挖数据价值 阿里云栖开年大会报道
  2. discuz 二次开发文章
  3. SharpStrike:基于C#实现的后渗透漏洞利用工具
  4. Markdown:数学公式(4)
  5. 去除VScode中的黄色警告波浪线问题
  6. iis自带的ftp服务器权限设置方法,IIS ftp 权限控制
  7. 新手篇 | K8S配置最佳实践
  8. 辽宁移动客服呼叫中心两级质检管理效果佳
  9. WebStorm 汉化教程-Mac
  10. 知识图谱嵌入:TransE算法原理及代码详解
  11. 小米笔记本UEFI关闭安全启动
  12. DN值,辐射率(Radiance),反射率
  13. 总资产周转率、资产负债率、销售净利率、资产收益率、净资产利润率、劳动生产率、人均利润率
  14. 魔兽争霸3 the oracle,疯狂SQL之魔兽争霸
  15. GD32W515实现NES模拟器
  16. Syntax Error Error No ESLint configuration found in statusGitmibsrc
  17. 微信图文素材中图片url替换
  18. Camtasia Studio 8
  19. 微信开发者工具控制台空白问题解决方案
  20. 工厂方法模式-----女娃造人的故事

热门文章

  1. 面试中常问的List去重问题,你都答对了吗?
  2. 20175213 2018-2019-2 《Java程序设计》第6周学习总结
  3. silverlight,WPF动画终极攻略之番外 3D切换导航篇(Blend 4开发)
  4. python列表操作
  5. 前端编码风格规范(3)—— JavaScript 规范
  6. 【转】一个优秀的Javascript框架--Prototype解说
  7. Promise.all捕获错误
  8. python如何创建模块教程_Python创建模块及模块导入的方法
  9. Java黑皮书课后题第5章:*5.16(找出一个整数的因子)编写程序,读入一个整数,然后以升序显示它的所有最小因子。例如,若输入的整数是120,那么输出就应该是:2、2、2、3、5
  10. 课堂派派典型用户和场景