Java NIO(二) ByteBuffer类源码分析

ByteBuffer看名字就知道它是用来缓冲字节的,我们先来看下ByteBuffer的用法。
    public static void main(String[] args) {
        // 申请1024字节空间
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        // 往byteBuffer写入123456
        byteBuffer.put("123456".getBytes());
        // 切换到读模式
        byteBuffer.flip();
        // buffer是否还有未读的字节
        while (byteBuffer.hasRemaining()) {
            // 读取一个字节
            System.out.print((char)byteBuffer.get());
        }
    }
我们先来看看ByteBuffer类的定义
    public abstract class ByteBuffer extends Buffer
        implements Comparable<ByteBuffer> {
        final byte[] hb; // 缓冲数组,只针对HeapByteBuffer                 
        final int offset; // 偏移量,从什么位置开始读写,一般为0
        boolean isReadOnly;  
    }
由ByteBuffer的定义我们可知它是一个抽象类,所以无法直接直接初始化ByteBuffer。它的初始化是通过allocate()方法或者allocateDirect()来进行初始化的。
同时我们可知ByteBuffer继承了Buffer类
    public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

    public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }
通过上面代码,ByteBuffer的初始化是通过初始化HeapByteBuffer或者DirectByteBuffer,一般常用的是HeapByteBuffer,所以我们先来看看HeapByteBuffer类,DirectByteBuffer放到后面来分析。
    class HeapByteBuffer extends ByteBuffer
由HeapByteBuffer定义可知,它继承了ByteBuffer类。我们再来看看它是如何初始化的
    // allocate使用的是这个构造函数,直接new byte[cap]来初始化数组hb
    HeapByteBuffer(int cap, int lim) {          
        super(-1, 0, lim, cap, new byte[cap], 0);//offset = 0 
    }

    HeapByteBuffer(byte[] buf, int off, int len) {
        super(-1, off, off + len, buf.length, buf, 0);
    }
    // 它是调用父类ByteBuffer的构造函数进行初始化的 
    ByteBuffer(int mark, int pos, int lim, int cap,  
                 byte[] hb, int offset)
    {
        //调用Buffer的构造函数,初始化mark,pos,lim,cap
        super(mark, pos, lim, cap); 
        this.hb = hb;
        this.offset = offset;
    }
    ByteBuffer(int mark, int pos, int lim, int cap) { 
        this(mark, pos, lim, cap, null, 0);
    }
下面我们来看看它的常用方法 
get方法
    // 获得hb[position+offset+1]的内容
    public byte get() {
        return hb[ix(nextGetIndex())];
    }
    // 获得下个位置的索引
    protected int ix(int i) {
        return i + offset;
    }
    // 在ByteBuffer类的父类Buffer中定义,获得position的下个位置
    final int nextGetIndex() {                           
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }
put方法
    //往byte[position++]的位置写入一个字节 
    public ByteBuffer put(byte x) {
        hb[ix(nextPutIndex())] = x;
        return this;
    } 
其他方法可以参照上一篇Buffer源码分析。最后我们来看下DirectByteBuffer类,它不是直接从JVM的堆上分配的,而是通过本地方法直接向操作系统申请内存。
    DirectByteBuffer(int cap) {               
        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);
        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }
它直接通过unsafe.allocateMemory(size)申请size大小的内存。分配完堆外内存后就会返回分配的堆外内存基地址,并将这个地址赋值给了address属性。这样我们后面通过JNI对这个堆外内存操作时都是通过这个address来实现的了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值