Netty 内存模型分析(一)ByteBuf总览

本文开始,将主要围绕netty相关知识展开,力求从宏观上把握整个内存结构。

ByteBuf

为了解决NIO 中 ByteBuffer 使用不当问题,例如读模式写模式不能同时进行,无法自动扩容等。Netty 新建了一种字节容器ByteBuf
ByteBuf 中,定义有 readerIndexwriterIndexcapacity
内部组织结构如下:
在这里插入图片描述
即可读区域、可写,以及容量。

对于 ByteBuf 更好的理解是一个接口(虽然是抽象类),因为里面大部分方法都是abstract。

AbstractByteBuf

AbstractByteBuf 作为 ByteBuf 子类,同样是一个抽象类,里面定义了 以下字段:

    int readerIndex;   // 读index
    int writerIndex;   // 写index
    private int markedReaderIndex;    // 标记 读index
    private int markedWriterIndex;       // 标记 写 index
    private int maxCapacity;   // 最大容量

AbstractByteBuf 中并没有定义具体的字节容器,而是针对上面几个字节容器的index进行操作,具体操作字节容器,则是抽象方法交给子类实现(因为有池化非池化,以及是否直接内存区别)。例如setBytes

    @Override
    public ByteBuf setBytes(int index, ByteBuf src, int length) {
        checkIndex(index, length);
        if (src == null) {
            throw new NullPointerException("src");
        }
        if (checkBounds) {
            checkReadableBounds(src, length);
        }
        setBytes(index, src, src.readerIndex(), length);
        src.readerIndex(src.readerIndex() + length);
        return this;
    }

对于 setBytes 方法:
则是作为抽象方法,由子类实现:
public abstract ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length);

AbstractReferenceCountedByteBuf

再往下看,再看一个子类为 AbstractReferenceCountedByteBuf。里面维持这个一个int类型的refCnt,即维持一个当前 ByteBuf 被引用次数。
ReferenceCounted 引用计数接口方法:

  1. int refCnt() 返回当前引用数
  2. ReferenceCounted retain() 增加引用数
  3. ReferenceCounted touch() 记录当前位置
  4. boolean release(); 减少引用数
Unpooled

往后,ByteBuf 又能分为Pooled和Unpooled,即池化和非池化内存,池化和非池化后面文章专门分析。

具体ByteBuf 又分为堆内存Heap 和 直接内存 Direct

UnpooledHeapByteBuf

看看其内部成员:

public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {

    private final ByteBufAllocator alloc;
    private byte[] array;  // netty byteBuf操作数
    private ByteBuffer tmpNioBuf;  // 对接java nio 的bytebuffer
	...
	}

具体看的有点懵,从writeByte(int value) 看看处理逻辑:
AbstractByteBufwriteByte

    @Override
    public ByteBuf writeByte(int value) {
        ensureWritable(1);   // 保证大小,看是否需要扩容
        setByte(writerIndex++, value);   // writerIndex 自增
        return this;
    }

AbstractByteBufsetByte(int index, int value)

    @Override
    public ByteBuf setByte(int index, int value) {
        checkIndex(index);   // 检查index
        _setByte(index, value);   // 具体
        return this;
    }

轮到具体子类 实现的方法 UnpooledHeapByteBuf_setByte(int index, int value)

    @Override
    protected void _setByte(int index, int value) {
        array[index] = (byte) value;
    }

所以实际是对array的字节数组进行操作。

tmpNioBuf 变量什么时候用呢?
由于netty是对java nio的封装,所以最终底层通信仍然是使用ByteBuffer 进行,所以就是将array数组转化为 ByteBuffer ,从 internalNioBuffer 可见一斑。

    private ByteBuffer internalNioBuffer() {
        ByteBuffer tmpNioBuf = this.tmpNioBuf;
        if (tmpNioBuf == null) {
            this.tmpNioBuf = tmpNioBuf = ByteBuffer.wrap(array);
        }
        return tmpNioBuf;
    }

还有一个内存分配器 ByteBufAllocator

每个ByteBuf 中都需要带有 ByteBufAllocator 说明他的内存分配类型,主要用于直接内存泄漏检测。
UnpooledByteBufAllocator 为例。

UnpooledByteBufAllocator

对于 堆内存,则是直接使用new方式获取一个 UnpooledByteBufAllocator:

    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

而直接内存,则会通过包装一层 内存泄漏检测,后面文章分析。

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        ByteBuf buf;
        if (PlatformDependent.hasUnsafe()) {
            buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }
UnpooledDirectByteBuf

UnpooledDirectByteBuf 主要成员变量为:

public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {

    private final ByteBufAllocator alloc;

    private ByteBuffer buffer;   // 操作的buffer
    private ByteBuffer tmpNioBuf;   // 用于转化为一个临时bytebuffer
    private int capacity;   // 当前容量
    private boolean doNotFree;  // 
    }

构造方法中,netty直接将java nio的 DirectByteBuffer
UnpooledDirectByteBuf 的 构造方法:

    protected UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(maxCapacity);
        ...
        setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
    }

ByteBufferallocateDirect 方法:

    public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

再看看 writeByte(int value) 方法:
前面和 UnpooledHeapByteBuf 基本一致,仍然看 _setByte(int index, int value) 方法:

    @Override
    protected void _setByte(int index, int value) {
        buffer.put(index, (byte) value);
    }

UnpooledDirectByteBuf 的扩容过程:
AbstractByteBufensureWritable 方法:

    @Override
    public ByteBuf ensureWritable(int minWritableBytes) {
        ...
        // 计算大小,保证是2的倍数
        int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);
        // 由子类实现具体大小.
        capacity(newCapacity);
        return this;
    }

UnpooledDirectByteBufcapacity 方法:

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

        int readerIndex = readerIndex();
        int writerIndex = writerIndex();

        int oldCapacity = capacity;
        if (newCapacity > oldCapacity) {
            ByteBuffer oldBuffer = buffer;
            ByteBuffer newBuffer = allocateDirect(newCapacity);
            oldBuffer.position(0).limit(oldBuffer.capacity());
            newBuffer.position(0).limit(oldBuffer.capacity());
            newBuffer.put(oldBuffer);
            newBuffer.clear();
            setByteBuffer(newBuffer);
        } else if (newCapacity < oldCapacity) {
            ByteBuffer oldBuffer = buffer;
            ByteBuffer newBuffer = allocateDirect(newCapacity);
            if (readerIndex < newCapacity) {
                if (writerIndex > newCapacity) {
                    writerIndex(writerIndex = newCapacity);
                }
                oldBuffer.position(readerIndex).limit(writerIndex);
                newBuffer.position(readerIndex).limit(writerIndex);
                newBuffer.put(oldBuffer);
                newBuffer.clear();
            } else {
                setIndex(newCapacity, newCapacity);
            }
            setByteBuffer(newBuffer);
        }
        return this;
    }

总体思路即申请一个新本地内存,然后使用ByteBufferput 方法将旧内存放到新申请的 直接内存的 ByteBuffer 中。

关注博主公众号: 六点A君。
哈哈哈,一起研究Netty:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值