Netty:ByteBuf和相关辅助类(二)

1.AbstractReferenceCountedByteBuf

从类的名字就可以看出该类主要是对引用进行计数,类似JVM内存回收的对象引用计数器,用于跟踪对象的分配和销毁,做自动内存回收。

/**
 * Abstract base class for {@link ByteBuf} implementations that count references.
 */
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {

    private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater;

    static {
        AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater =
                PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
        if (updater == null) {
            updater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
        }
        refCntUpdater = updater;
    }

    private volatile int refCnt = 1;

refCntUpdater,它是AtomicIntegerFieldUpdater类型变量,通过原子的方式对成员变量进行更新等操作,以实现线程安全,消除锁。

volatile修饰的refCnt字段用于跟踪对象的引用次数,使用volatile是为了解决多线程并发访问的可见性问题。

1.1 对象引用计数器

每调用一次retain方法,引用计数器就会加一,由于可能存在多线程并发调用的场景,所以它的累加操作必须是线程安全的。

    @Override
    public ByteBuf retain() {
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, 1);
            }
            if (refCnt == Integer.MAX_VALUE) {
                throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1);
            }
            if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
                break;
            }
        }
        return this;
    }

1.2释放引用计数器

    @Override
    public final boolean release() {
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, -1);
            }

            if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
                if (refCnt == 1) {
                    deallocate();
                    return true;
                }
                return false;
            }
        }
    }

2.UnpooledHeapByteBuf

/**
 * Big endian Java heap buffer implementation.
 */
public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {

    private final ByteBufAllocator alloc;
    private byte[] array;
    private ByteBuffer tmpNioBuf;

它聚合了一个ByteBufAllocator,用于UnpooledHeapByteBuf 的内存分配;

byte[] array数组作为缓冲区;

最后定义一个ByteBuffer类型的tmpNioBuf变量用于实现Netty ByteBuf到JDK NIO ByteBuffer的转换。

2.2 动态扩展缓冲区

需要指出的是,当动态扩容完成后,需要将原来的视图tmpNioBuf设置为空。

    @Override
    public ByteBuf capacity(int newCapacity) {
        ensureAccessible();
        if (newCapacity < 0 || newCapacity > maxCapacity()) {
            throw new IllegalArgumentException("newCapacity: " + newCapacity);
        }

        int oldCapacity = array.length;
        if (newCapacity > oldCapacity) {
            byte[] newArray = new byte[newCapacity];
            System.arraycopy(array, 0, newArray, 0, array.length);
            setArray(newArray);
        } else if (newCapacity < oldCapacity) {
            byte[] newArray = new byte[newCapacity];
            int readerIndex = readerIndex();
            if (readerIndex < newCapacity) {
                int writerIndex = writerIndex();
                if (writerIndex > newCapacity) {
                    writerIndex(writerIndex = newCapacity);
                }
                System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
            } else {
                setIndex(newCapacity, newCapacity);
            }
            setArray(newArray);
        }
        return this;
    }

如果新的容量小于读索引,说明没有可读的字节数组需要复制到新创建的缓冲区中,将读写索引设置为新的容量即可。最后调用setArray方法替换原来的数组。

2.3字节数组复制

  private void setArray(byte[] initialArray) {
        array = initialArray;
        tmpNioBuf = null;
    }

2.4转换成JDK ByteBuffer

    @Override
    public ByteBuffer nioBuffer(int index, int length) {
        ensureAccessible();
        return ByteBuffer.wrap(array, index, length).slice();
    }
    public static ByteBuffer wrap(byte[] array,
                                    int offset, int length)
    {
        try {
            return new HeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }

3.PooledByteBuf内存池原理分析

3.1 PoolArena

abstract class PoolArena<T> {

    static final int numTinySubpagePools = 512 >>> 4;

    final PooledByteBufAllocator parent;

    private final int maxOrder;
    final int pageSize;
    final int pageShifts;
    final int chunkSize;
    final int subpageOverflowMask;
    final int numSmallSubpagePools;
    private final PoolSubpage<T>[] tinySubpagePools;
    private final PoolSubpage<T>[] smallSubpagePools;

    private final PoolChunkList<T> q050;
    private final PoolChunkList<T> q025;
    private final PoolChunkList<T> q000;
    private final PoolChunkList<T> qInit;
    private final PoolChunkList<T> q075;
    private final PoolChunkList<T> q100;

3.2PoolChunk

3.3PoolSubpage

final class PoolSubpage<T> {

    final PoolChunk<T> chunk;
    private final int memoryMapIdx;
    private final int runOffset;
    private final int pageSize;
    private final long[] bitmap;

    PoolSubpage<T> prev;
    PoolSubpage<T> next;

    boolean doNotDestroy;
    int elemSize;
    private int maxNumElems;
    private int bitmapLength;
    private int nextAvail;
    private int numAvail;

3.4内存回收策略

4.PooledDirectByteBuf源码分析

PooledDirectByteBuf基于内存池实现,与UnPooledDirectByteBuf的唯一不同就是缓冲区的分配和销毁策略不同,其他功能都是等同的,也就是说,两者唯一不同的就是内存分配策略不同。

4.1创建字节缓冲区

final class PooledDirectByteBuf extends PooledByteBuf<ByteBuffer> {

    private static final Recycler<PooledDirectByteBuf> RECYCLER = new Recycler<PooledDirectByteBuf>() {
        @Override
        protected PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    };

    static PooledDirectByteBuf newInstance(int maxCapacity) {
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.setRefCnt(1);
        buf.maxCapacity(maxCapacity);
        return buf;
    }

2.复制新的字节缓冲区实例

    @Override
    public ByteBuf copy(int index, int length) {
        checkIndex(index, length);
        ByteBuf copy = alloc().directBuffer(length, maxCapacity());
        copy.writeBytes(this, index, length);
        return copy;
    }

    @Override
    public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        validate(initialCapacity, maxCapacity);
        return newDirectBuffer(initialCapacity, maxCapacity);
    }
    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            if (PlatformDependent.hasUnsafe()) {
                buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
            } else {
                buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
            }
        }

        return toLeakAwareBuffer(buf);
    }

备注:文章参考《Netty权威指南》,作者:李林锋。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果需要从NettyByteBuf中读取进制数据,可以使用`readBytes`方法。该方法有多个重载形式,可以读取指定长度的字节数据,也可以读取全部可读字节数据。以下是一个示例代码: ```java ByteBuf buf = ...; // 假设已经创建好了ByteBuf对象 byte[] data = new byte[buf.readableBytes()]; // 创建一个字节数组,长度为当前可读字节数 buf.readBytes(data); // 从ByteBuf中读取所有可读字节数据到字节数组中 ``` 上面的代码创建了一个字节数组,长度为当前ByteBuf中可读的字节数。然后使用`readBytes`方法从ByteBuf中读取所有可读字节数据到字节数组中。 如果只需要读取部分字节数据,可以使用`readBytes`方法的另一个重载形式,该方法接受一个长度参数,表示需要读取的字节数。例如,以下代码从ByteBuf中读取了前5个字节数据到字节数组中: ```java ByteBuf buf = ...; // 假设已经创建好了ByteBuf对象 byte[] data = new byte[5]; // 创建一个长度为5的字节数组 buf.readBytes(data); // 从ByteBuf中读取前5个字节数据到字节数组中 ``` 需要注意的是,从ByteBuf中读取数据时,如果数据不足,会抛出`IndexOutOfBoundsException`异常。因此在读取数据之前,需要先检查可读字节数是否足够。可以使用`readableBytes`方法获取当前ByteBuf中可读的字节数。例如,以下代码在读取数据之前先检查可读字节数是否足够: ```java ByteBuf buf = ...; // 假设已经创建好了ByteBuf对象 int readableBytes = buf.readableBytes(); // 获取当前可读字节数 if (readableBytes >= 5) { byte[] data = new byte[5]; // 创建一个长度为5的字节数组 buf.readBytes(data); // 从ByteBuf中读取前5个字节数据到字节数组中 } else { // 可读字节数不足,处理异常情况 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值