• 理解 ByteBuffer


    理解 ByteBuffer

    ByteBuffer 译为 字节缓冲区 , 是 Java nio 包下提供的一个抽象类 java.nio.ByteBuffer

    缓冲区即预先分配的内存,是从内存中提前划分出的一块区域。

    直接已知子类是 MappedByteBuffer

    public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>
    
    • 1
    直接与非直接内存/字节缓冲区

    在这里插入图片描述

    给定一个直接内存,Java 虚拟机将尽最大努力在之上直接进行本地 I/O 操作。

    也就是说,使用 ByteBuffer 可以避免 直接内存堆内存数据拷贝过程。即常说的 零拷贝

    可以使用 allocateDirect 方法来创建直接字节缓冲区。该方法返回的缓冲区通常比堆内存的缓冲区有更高的分配和释放成本

    直接缓冲区的内容可能驻留在正常的堆垃圾回收之外,它们对程序内存占用的影响并不明显。通常建议直接缓冲区主要分配给受底层系统 I/O 操作影响较多的,大型、长期存在的数据。仅在直接缓冲区对系统性能优化是可以预见的情况下才进行使用(不要瞎优化,负优化,分配和释放比堆内申请要慢

    也可以通过将文件的区域直接 mapping 到内存来创建直接字节缓冲区,字节缓冲区是直接的还是非直接的可以通过调用 isDirect 来确定。

    创建 ByteBuffer

    ByteBuffer 是一个抽象类,它为我们提供了静态工厂方法来创建实例,如下图

    在这里插入图片描述

    allocation 分配

    分配一个具备特定容量的 ByteBuffer

    allocate 会在 Java 堆中进行分配,返回的是一个 java.nio.HeapByteBuffer 实例

    ByteBuffer buffer = ByteBuffer.allocate(10);
    
    • 1

    allocateDirect 会在直接内存分配,返回的是一个 java.nio.DirectByteBuffer 实例

    ByteBuffer buffer = ByteBuffer.allocateDirect(10);
    
    • 1

    wrapping 包装

    从现有 array 数组进行包装,所有对于 byte[] array 的操作都会反应到 bytebuffer 字节缓冲区中,反之亦然

    byte[] bytes = new byte[10];
    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    
    • 1
    • 2

    上面的代码等价于

    ByteBuffer buffer = ByteBuffer.wrap(bytes, 0, bytes.length);
    
    • 1
    ByteBuffer 四个基本索引

    这四个索引记录了底层数据的状态

    • Capacity: 缓冲区可以容纳的最大数据元素个数
    • Limit: 读取或者写入的最大位置
    • Position: 当前读取或者写入的位置
    • Mark: 一个被标记的 position 值 (相当于记录 position 之前的一个状态)

    这四个索引的关系是

        // Invariants: mark <= position <= limit <= capacity
        private int mark = -1;
        private int position = 0;
        private int limit;
        private int capacity;
    
        // Used only by direct buffers
        // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
        long address;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这些都是定义在继承的父类 java.nio.Buffer

    当我们创建一个 ByteBuffer 实例时,mark 是未定义的初始值 -1,position 是 0 ,limit = capacity 。

    例如

    ByteBuffer buffer = ByteBuffer.allocate(10);
    int position = buffer.position(); // 0
    int limit = buffer.limit();       // 10
    int capacity = buffer.capacity(); // 10
    
    • 1
    • 2
    • 3
    • 4

    这容量是只读的,无法修改,可以使用以下方法改变 position 和 limit

    buffer.position(2);
    buffer.limit(5);
    
    • 1
    • 2
    ByteBuffer 索引相关方法

    从概念上讲,ByteBuffer 是对字节数组进行了包装,提供了一系列方法对底层的数据进行读写操作,且这一系列的方法高度依赖于维护的索引。

    我们可以抽象理解为,ByteBuffer 就是 字节数组 + 额外索引 的一个容器

    byte[] array + index
    
    • 1

    在这里插入图片描述

    上面是索引相关操作的方法,大体可以分为四部分:

    1. 基本
    2. 标记和重置
    3. 清除、翻转、倒带、压紧
    4. 剩余

    标记和重置

    mark 方法和 reset 方法时配合使用的,如果没有进行过 mark ,直接执行 reset 将会抛出 InvalidMarkException

     		public final Buffer reset() {
            int m = mark;
            if (m < 0)
                throw new InvalidMarkException();
            position = m;
            return this;
        }
    
        public final Buffer mark() {
            mark = position;
            return this;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    清除、翻转、倒带、压紧

    clear 清除,相当于重置为初始状态,如果要重用缓存区使用 clear 方法最方便

        public final Buffer clear() {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    flip 翻转,限制更新为当前位置,当前位置重置为 0,会从写模式切换为读模式,一般会配合 compact 方法使用

        public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    rewind 倒带,重置 position 为 0 mark 为 -1 未定义

        public final Buffer rewind() {
            position = 0;
            mark = -1;
            return this;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    compact 方法会将 limit 置为 容量, position 为变为剩余 limit - position 并重置 mark

    剩余

    public final int remaining() {
        return limit - position;
    }
    public final boolean hasRemaining() {
        return position < limit;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    使用 ByteBuffer 传输数据

    ByteBuffer 提供了传输或者获取数据的一系列方法

    在这里插入图片描述

    关于传输字节数据

    public abstract byte get();
    public abstract ByteBuffer put(byte b);
    public abstract byte get(int index);
    public abstract ByteBuffer put(int index, byte b);
    
    • 1
    • 2
    • 3
    • 4

    提供了两个版本的方法,一个有 index 参数,一个没有。

    没有传入 index 的方法会对当前 position 位置的数据元素进行操作,之后 position++

    传入 index 则对 index 位置元素进行操作,且不会改变 position

  • 相关阅读:
    GEE ——绘制二元分类的特征 (ROC) 曲线、计算曲线下面积 (AUC)
    vs2019 无法打开QT的UI文件
    java springboot通过EnableConfigurationProperties全局声明bean并处理装配
    [免费专栏] ATTACK安全之检测车机中ADB远程调试控制Android系统攻击
    安卓APP源码和设计报告——智能垃圾桶
    UI设计师的发展前景是否超越了平面设计?
    预制菜行业数据分析(京东数据挖掘)
    【干货技巧】最新 Java 后端面试系列干货,都在这了!
    Netty
    怎么把视频压缩到最小?快把这些方法收好
  • 原文地址:https://blog.csdn.net/w903328615/article/details/127694831