美文网首页
Netty 内存分配 ByteBuf

Netty 内存分配 ByteBuf

作者: 剑道_7ffc | 来源:发表于2020-04-13 08:10 被阅读0次

初识ByteBuf

ByteBuf是io和应用程序的中间层;读数据时,ByteBuf先从io中读取,在发送给应用程序;写数据时,ByteBuf先从应用程序中获取,再向io写入。

ByteBuf 的基本结构

image.png
3个区间

0-readerIndex(读区间的开始位置):丢弃的数据区
readerIndex-writerIndex(写区间的开始位置):可读数据区
writerIndex-capacity:可写数据区

容量

capactiy:当前buffer的容量
maxCapacity:可扩容的最大缓冲区的容量

ByteBuf 的重要 API

ByteBuf的基本实现是AbstractByteBuf

ByteBuf 的基本分类

Pooled(池化内存):基于预先分配好的内存空间
Unsafe:基于对象的内存地址进行读写操作,jdk底层负责io操作的对象
Direct(堆外内存):基于物理地址进行读写操作,不在jvm堆内存中,需要手动释放
综上所述, ByteBuf 一共会有六种组合:Pooled 池化内存和 Unpooled 非池化内存;Unsafe 和非 Unsafe;Heap 堆内存和 Direct 堆外内存


image.png

ByteBufAllocator 内存管理器

ByteBufAllocator 的基本实现类 AbstractByteBufAllocator

Unpooled 非池化内存分配

堆内内存的分配

heapBuffer 的分配逻辑
    private UnpooledHeapByteBuf(
            ByteBufAllocator alloc, byte[] initialArray, int readerIndex, int writerIndex, int maxCapacity) {
        setArray(initialArray);
        setIndex(readerIndex, writerIndex);
    }

初始化一个数组

UnpooledUnsafeHeapByteBuf 和 UnpooledHeapByteBuf区别

根本区别在io读写
1 UnpooledHeapByteBuf
io.netty.buffer.UnpooledHeapByteBuf#getByte,直接从数组索引取值

    static byte getByte(byte[] memory, int index) {
        return memory[index];
    }

2 UnpooledUnsafeHeapByteBuf
io.netty.buffer.UnsafeByteBufUtil#getByte(byte[], int)从unsafe取值

    static byte getByte(byte[] data, int index) {
        return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
    }

堆外内存的分配

UnpooledDirectByteBuf和UnpooledUnsafeDirectByteBuf区别

1 UnpooledDirectByteBuf
通过数组下标来取数据
2 UnpooledUnsafeDirectByteBuf

    final void setByteBuffer(ByteBuffer buffer, boolean tryFree) {
        this.buffer = buffer;
        memoryAddress = PlatformDependent.directBufferAddress(buffer);
    }
    private static long getLong(Object object, long fieldOffset) {
        return UNSAFE.getLong(object, fieldOffset);
    }

unsafe直接通过buffer的内存地址和偏移量来取数据

Pooled 池化内存分配

PooledByteBufAllocator 简述

FastThreadLocal:通过数组来存储数据且执行性能较快的本地线程
PoolArenaMetric:池子的数据记录

        protected synchronized PoolThreadCache initialValue() {
            final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
            final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);

            return new PoolThreadCache(
                    heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
                    DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
        }
        DEFAULT_NUM_HEAP_ARENA = Math.max(0,
                SystemPropertyUtil.getInt(
                        "io.netty.allocator.numHeapArenas",
                        (int) Math.min(
                                defaultMinNumArena,
                                runtime.maxMemory() / defaultChunkSize / 2 / 3)));
        DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
                SystemPropertyUtil.getInt(
                        "io.netty.allocator.numDirectArenas",
                        (int) Math.min(
                                defaultMinNumArena,
                                PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));

EventLoopGroup 给分配线程时默认线程数也是 CPU 核数2,area的默认值也是CPU 核数2,这样做是每个线程都有一个独立的Arena,保障在内存分配时不被加锁。

image.png

DirectArena 内存分配流程

Arena 分配内存的基本流程有三个步骤:

    PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
        PooledByteBuf<T> buf = newByteBuf(maxCapacity);
        allocate(cache, buf, reqCapacity);
        return buf;
    }

1、从对象池里拿到 PooledByteBuf 进行复用;

    static PooledUnsafeDirectByteBuf newInstance(int maxCapacity) {
        PooledUnsafeDirectByteBuf buf = RECYCLER.get();
        buf.reuse(maxCapacity);
        return buf;
    }

2、从缓存中进行内存分配;
3、从内存堆里进行内存分配。

内存池的内存规格

在 Netty 内存池中主要设置了四种规格大小 的内存:tiny 是指 0-512Byte 之间的规格大小,small 是指 512Byte-8KB 之间的规格大小,normal 是指 8KB-16MB 之 间的规格大小,huge 是指 16MB 以上.为什么 Netty 会选择这些值作为一个分界点呢?其实在 Netty 底层还有一个内 存单位的封装,为了更高效地管理内存,避免内存浪费,把每一个区间的内存规格由做了细分。默认情况下,Netty 将内存规格划分为 4 个部分。Netty 中所有的内存申请是以 Chunk 为单位向内存申请的,大小为 16M,后续的所有内 存分配都是在这个 Chunk 里面的操作。8K 对应的是一个 Page,一个 Chunk 会以 Page 为单位进行切分,8K 对应 Chunk 被划分为 2048 个 Page。小于 8K 的对应的是 SubPage。例如:我们申请的一段内存空间只有 1K,却给我们分配了一 个 Page,显然另外 7K 就会被浪费,所以就继续把 Page 进行划分,来节省空间.


image.png

相关文章

网友评论

      本文标题:Netty 内存分配 ByteBuf

      本文链接:https://www.haomeiwen.com/subject/wxpwmhtx.html