bytebuffer转图片_图解ByteBuffer和ByteBuf

本文介绍了Netty中的ByteBuf与Java NIO的ByteBuffer的区别和用法,包括ByteBuffer的写入数据、切换读写模式、读取数据以及清除缓冲区的操作。同时,对比了ByteBuf的读写索引分离策略和动态扩容能力,以及Netty中不同类型的ByteBuf子类。
摘要由CSDN通过智能技术生成

前言:

最近在学习 netty,看了 Netty 权威指南,其中源码第一篇就讲到了 ByteBuffer 和 byteBuf 的区别,这里就总结一下。

ByteBuffer

bytebuffer 是 Java NIO 里面提供的字节容器。有一个指针用于处理读写操作,每次读写的时候都需要调用 flip()或是 clear()方法,不然将会报异常。

部分源码:

public abstract class ByteBuffer  extends Buffer{

}

public abstract class Buffer {

// Invariants: mark <= position <= limit <= capacity

private int mark = -1;

private int position = 0;

private int limit;

private int capacity;

}

成员变量关系

属性

描述

mark

调用 mark()方法的话,mark 值将存储当前 position 的值,等下次调用 reset()方法时,会设定 position 的值为之前的标记值

position

用来表示 bytes 的容量,那么可以想像 capacity 就等于 bytes.size(),此值在初始化 bytes 后,是不可变的

limit

用来表示 bytes 实际装了多少数据,可以容易想像得到 limit <= capacity,此值是可灵活变动的

capacity

用来表示在哪个位置开始往 bytes 写数据或是读数据,此值是可灵活变动的

他们之间的关系:mark <= position <= limit <= capacity

创建一个 bytebuffer

ByteBuffer bf = ByteBuffer.allocate(10);

position,limit 和 capacity 图解如下:

「写入数据到 bytebuffer」

bf.put((byte)’H’)

.put((byte)’e’)

.put((byte)’l’)

.put((byte)’l’)

.put((byte)’o’)

在操作 bytebuffer 的时候,每次往里面写入一个 byte,position 则会后移一位。

「使用 flip() 刷新缓冲区为 读模式」

bf.flip()

bytebuffer 有两种模式,分别是写模式和读模式,这两种模式通过使用 flip 方法进行模式。

如上将缓冲区切换为读模式,则 position 变成了初值位置 0,而 limit 变成了写模式下 position 位置。

「读取数据」

bf.get()

调用 get()获取缓冲区中的一个 byte。

「清除缓冲区」

bf.clear()

tips: 这个方法简单理解就是复位(Reset) 但不会清除数据(position=0, limit=capacity)

JDK 自带的 ByteBuffer 并不足够完美,它有以下缺陷:

摘抄自《Netty 权威指南》

ByteBuffer 长度固定,一旦分配完成,它的容量不能动态扩展和收缩,当需要编码的 POJO 对象大于 ByteBuffer 的容量时,会发生索引越界异常;

ByteBuffer 只有一个标识位控的指针 position,读写的时候需要手工调用 flip()和 rewind()等,使用者必须小心谨慎地处理这些 API,否则很容易导致程序处理失败;

ByteBuffer 的 API 功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。

ByteBuf

bytebuf 是 Netty 里的封装的数据缓存区,区别于 bytebuffer 里需要 position、limit、capacity 等属性来操作 bytebuffer 数据读写,而 bytebuf 里面则是通过 两个指针协助缓存区的读写操作,分别为 readIndex 和 writerIndex 。

在创建 bytebuf 的时候,readIndex 和 writerIndex 的值都是 0,但随着有数据被写入 writerIndex 会增加,读取数据的时候 readIndex 也会增加, 但是 readIndex 不会超过 writerIndex。

图解:

创建一个 bytebuf

ByteBuf bf= Unpooled.buffer(10,100)

write:写入 N 个字节之后 ByteBuf

read:读取 M 个字节后(M

使用 demo:

ByteBuf bf= Unpooled.buffer(10,100);

bf.writeBoolean(true);

bf.writeInt(666);

bf.writeInt(777);

bf.writeInt(888);

System.out.println(bf.readBoolean());

System.out.println(bf.readInt());

System.out.println(bf.readInt());

System.out.println(bf);

ByteBuf 的基本分类:

AbstractByteBuf 之下有众多子类,大致可以从三个维度来进行分类,分别如下:

「Pooled」:池化内存,就是从预先分配好的内存空间中提取一段连续内存封装成一个ByteBuf 分给应用程序使用。

「Unsafe」:是JDK 底层的一个负责IO 操作的对象,可以直接拿到对象的内存地址,基于内存地址进行读写操作。

「Direct」:堆外内存,是直接调用JDK 的底层API 进行物理内存分配,不在JVM 的堆内存中,需要手动释放。

ByteBuf 最基本的读写API 操作在AbstractByteBuf 中已经实现了,其众多子类采用不同的策略来分配内存空间,下面对重要的几个子类总结如下:

PooledHeapByteBuf :池化的堆内缓冲区

PooledUnsafeHeapByteBuf :池化的Unsafe 堆内缓冲区

PooledDirectByteBuf :池化的直接(堆外)缓冲区

PooledUnsafeDirectByteBuf :池化的Unsafe 直接(堆外)缓冲区

UnpooledHeapByteBuf :非池化的堆内缓冲区

UnpooledUnsafeHeapByteBuf :非池化的Unsafe 堆内缓冲区

UnpooledDirectByteBuf :非池化的直接(堆外)缓冲区

UnpooledUnsafeDirectByteBuf :非池化的Unsafe 直接(堆外)缓冲区

ByteBuffer 和 ByteBuf 的区别

Netty 的 ByteBuf 采用了读写索引分离的策略(readerIndex 与 writerIndex),一个初始化(里面尚未有任何数据)的 ByteBuf 的 readerIndex 与 writerIndex 值都为 0

当读索引与写索引处于同一个位置时,如果继续读取,那么就会抛出 IndexOutOfBoundsException。

ByteBuffer 只有一个标识位置的指针,读写的时候需要手动的调用 flip()和 rewind()等,否则很容易导致程序处理失败。而 ByteBuf 有两个标识位置的指针,一个写 writerIndex,一个读 readerIndex,读写的时候不需要调用额外的方法。

ByteBuffer 必须自己长度固定,一旦分配完成,它的容量不能动态扩展和收缩;ByteBuf 默认容器大小为 256,支持动态扩容,在允许的最大扩容范围内(Integer.MAX_VALUE)。

NIO 的 SocketChannel 进行网络读写时,操作的对象是 JDK 标准的 java.nio.byteBuffer。由于 Netty 使用统一的 ByteBuf 替代 JDK 原生的 java.nio.ByteBuffer,所以 ByteBuf 中定义了 ByteBuffer nioBuffer()方法将 ByteBuf 转换成 ByteBuffer。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值