网站首页 > 技术文章 正文
使用get函数获取
将ByteArray转byte[],大部分人第一时间会使用get函数
public ByteBuffer get(byte[] dst, int offset, int length) {
checkBounds(offset, length, dst.length);
if (length > remaining())
throw new BufferUnderflowException();
int end = offset + length;
for (int i = offset; i < end; i++)
dst[i] = get();
return this;
}
public ByteBuffer get(byte[] dst) {
return get(dst, 0, dst.length);
}
这两个get函数,都是先创建byte[]数组并传入,然后将数据写入。 注意这里就需要知道创建的byte[]数组的长度,一般使用
int len = byteBuffer.limit() - byteBuffer.position();
这里就涉及到ByteArray的几个属性
- capacity:容量,不能改变,超过容量会报错
- limit:第一个不可读写的位置,即最后一个数据位置的后一位。实际上就是数据长度,它不能超过容量。
- position:当前的位置。
那么它们三个是如何变化的?看下面的图片
可以看到当我们写入数据的时候,position会移动。当我们写完数据准备读数据时,需要先调用flip将position移至开头,这样才能读出完整的数据。
但是我们也可以从中间开始读数据,所以我们要读出的数据长度是limit-position,而不仅仅是limit
使用array函数获取
但是ByteArray还有另外一个函数,之前很少注意到,那就array
public final byte[] array() {
if (hb == null)
throw new UnsupportedOperationException();
if (isReadOnly)
throw new ReadOnlyBufferException();
return hb;
}
这个直接得到一个byte[],那么直接使用这个不是更好么?
我们可以看到这个byte数组是ByteArray的一个属性hb,且这个hb有为null的时候。
那么这个hb是什么?我们看看源码
final byte[] hb; // Non-null only for heap buffers
可以看到只有在heap buffer中才不为空,那么什么是heap buffer?
这里就涉及到ByteArray的实现,通过代码可以看到ByteArray是一个抽象类,我们实际使用的都是它的实现类HeapByteBuffer和DirectByteBuffer。
HeapByteBuffer和DirectByteBuffer
我们创建ByteArray的时候会使用allocate函数,在ByteArray里有两个函数
public static ByteBuffer allocateDirect(int capacity) {
// Android-changed: Android's DirectByteBuffers carry a MemoryRef.
// return new DirectByteBuffer(capacity);
DirectByteBuffer.MemoryRef memoryRef = new DirectByteBuffer.MemoryRef(capacity);
return new DirectByteBuffer(capacity, memoryRef);
}
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
可以看到这两个函数就是分别创建HeapByteBuffer和DirectByteBuffer
那么它们两个有什么区别?
DirectByteBuffer不是分配在堆上的,它不被GC直接管理(但Direct Buffer的JAVA对象是归GC管理的,只要GC回收了它的JAVA对象,操作系统才会释放Direct Buffer所申请的空间),它似乎给人感觉是“内核缓冲区(buffer in kernel)”。HeapByteBuffer则是分配在堆上的,或者我们可以简单理解为Heap Buffer就是byte[]数组的一种封装形式,查看JAVA源代码实现,HeapByteBuffer也的确是这样。 说白了就是HeapByteBuffer是在JVM堆内存中分配会被JVM管理回收,但是DirectByteBuffer是直接由系统内存进行分配,不被JVM管理。通过上面的区别看到:
1、创建和释放DirectByteBuffer的代价比HeapByteBuffer得要高,因为JVM堆中分配和释放内存肯定比系统分配和创建内存高效
2、因为平时的read/write,都会在I/O设备与应用程序空间之间经历一个“内核缓冲区”。 DirectByteBuffer就好比是“内核缓冲区”上的缓存,不直接受GC管理;而Heap Buffer就仅仅是byte[]字节数组的包装形式。因此把一个Direct Buffer写入一个Channel的速度要比把一个HeapByteBuffer写入一个Channel的速度要快。
所以这两个类操作起来各有好处,要视情况而定,一般如果是一个ByteBuffer经常被重用的话,就可以使用DirectByteBuffer对象。如果是需要经常释放和分配的地方用HeapByteBuffer对象。
从它们处理get或put数据上就可以看到差别。这里以get为例:
//HeapByteBuffer
public byte get() {
return hb[ix(nextGetIndex())];
}
HeapByteBuffer的get很简单,就是直接从hb中获取相应位置的数据
//DirectByteBuffer
public final byte get() {
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
return get(ix(nextGetIndex()));
}
private byte get(long a) {
return Memory.peekByte(a);
}
DirectByteBuffer的get函数最终是使用Memory的peekByte来获取的。所以DirectByteBuffer的操作都是通过Memory进行处理的。
结论
所以我们知道HeapByteBuffer是分配在堆上的,本质上就是byte[],所以它的hb不为null,就是这个byte[]。 所以HeapByteBuffer可以直接使用array函数得到byte[] 但是DirectByteBuffer就不行,因为它的hb是null的。所以DirectByteBuffer只能使用get函数获取byte[]
这里注意,在高版本(至少android 29,具体从哪个版本不清楚)上DirectByteBuffer的hb已经不是null了,且也有数据,但是这个数据是否是正常的数据还未进行调研,为了保险起见还是使用get函数安全一些
作者:BennuCTech
链接:https://juejin.cn/post/7074418793782444039
- 上一篇: 运行时异常和普通异常有什么区别?
- 下一篇: 经典java面试题,看你会几题?实习生必问
猜你喜欢
- 2024-10-04 Netty:数据传输载体- ByteBuf 详解
- 2024-10-04 JDK源码阅读:ByteBuffer(jdk源码阅读顺序以及优先级)
- 2024-10-04 初级java程序员面试题 项目业务逻辑问题 培训班 基础题 开发
- 2024-10-04 好文推荐:从JVM模型谈十种内存溢出及解决方法
- 2024-10-04 「系列教程」手写RPC框架(2) BIO模型替换成NIO模型学习
- 2024-10-04 详解Java NIO中的Buffer、Channel 和 Selector
- 2024-10-04 Java常见的面试题(java面试常见问题与答案)
- 2024-10-04 「系列教程」手写RPC框架(2) NIO模型学习
- 2024-10-04 什么是缓冲区溢出攻击?(缓冲区溢出攻击代名词)
- 2024-10-04 java教程、JAVA学习 |JAVA面试题大全(高级)
- 10-02基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- 10-02Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- 10-02AWD-LSTM语言模型是如何实现的_lstm语言模型
- 10-02NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 10-02使用ONNX和Torchscript加快推理速度的测试
- 10-02tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- 10-02Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- 10-02Gateway_gateways
- 最近发表
-
- 基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- AWD-LSTM语言模型是如何实现的_lstm语言模型
- NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 使用ONNX和Torchscript加快推理速度的测试
- tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- Gateway_gateways
- Coze开源本地部署教程_开源canopen
- 扣子开源本地部署教程 丨Coze智能体小白喂饭级指南
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)