JAVA NIO -- ByteBuffer

本文深入解析Java中的Buffer概念,包括其核心属性如position、mark、limit及capacity,并介绍读写模式切换方法。同时,详细探讨ByteBuffer及其子类HeapByteBuffer和DirectByteBuffer的特点与应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Buffer(缓冲区)

Buffer是缓冲区的抽象类,他定义了缓冲区的读写模式。

主要属性
  • position:读写指针的位置
  • mark: 标记位置
  • limit:读写界限(超出不可读写)
  • capacity:缓冲区最大容量(不可更改)
读写模式

因为缓冲区的只有一个指针(position),所以他需要有方法在读写模式之间切换

  • 写模式切换到读模式 - 在写完数据后,调用flip(),然后即可读取数据
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
  • 读模式切换到写模式 - 在读取完数据后,调用clear() ,可覆盖写入
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
  • 标记重置模式,mark()与reset()配合使用,可实现重置指针的功能(读写)
    public final Buffer mark() {
        mark = position;
        return this;
    }

    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }
  • 倒带(重读或者重写)
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
ByteBuffer

字节缓冲区的抽象类,子类有以堆内存做缓冲区的HeapByteBuffer和以堆外的系统本地内存做缓冲区的DirectByteBuffer

主要属性

除了父类的四个属性外,ByteBuffer还新增了三个属性

  • hb:用来存储的字节数组
  • offset:对于字节数组的起始偏移量(这个属性可以使用同一个字节数组封装两个独立的ByteBuffer,每个ByteBuffer的容量是字节数组长度的一半)
  • isReadOnly:是否只读
主要方法
  • 生成缓冲区
    // 返回一个直接缓冲区
    public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

    // 返回一个堆内存缓冲区
    public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

    //把一个二进制数组包装成堆内存缓冲区
    public static ByteBuffer wrap(byte[] array, int offset, int length)
    {
        try {
            return new HeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }
  • 副本与分片(与原缓冲区内容共享,只是有各自独立的指针和标记)
    // 返回一个从当前position指针位置开始的分片,重置指针和标记
    public abstract ByteBuffer slice();

    // 返回一个读写副本
    public abstract ByteBuffer duplicate();

    // 返回一个只读副本
    public abstract ByteBuffer asReadOnlyBuffer();
  • 读写
    // 从当前position指针 + 1位置读取一个字节
    public abstract byte get();

    // 向当前position指针 + 1位置写入一个字节
    public abstract ByteBuffer put(byte b);

    // 从指定指针位置index处读取一个字节
    public abstract byte get(int index);

    //向指定指针index位置写入一个字节
    public abstract ByteBuffer put(int index, byte b);

    //从当前指针处读取length长度的字节,写入目标数组
    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;
    }

    ...


HeapByteBuffer和DirectByteBuffer(-XX:MaxDirectMemorySize参数来设置堆外内存的大小)
优缺点比较
  • 创建和释放Direct Buffer的代价比Heap Buffer得要高
  • 由于DirectByteBuffer省去了从对内存拷贝到本地内存的操作,所以在I/O操作中会更快
  • 由于DirectByteBuffer是堆外内存,减少了对堆内存的使用(DirectByteBuffer对象本身很小),所以会对堆内存的垃圾回收有所改善
Netty

由于HeapByteBuffer和DirectByteBuffer各有优缺点,在netty中,通过内存池复用DirectByteBuffer的方式,改善了DirectByteBuffer创建和释放代价高的缺点,更有效的使用DirectByteBuffer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值