zoukankan      html  css  js  c++  java
  • Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述

     

    Netty源码分析第五章: ByteBuf

     

    第五节: directArena分配缓冲区概述

     

    上一小节简单分析了PooledByteBufAllocator中, 线程局部缓存和arean的相关逻辑, 这一小节简单分析下directArena分配缓冲区的相关过程

    回到newDirectBuffer中:

    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 = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
            } else {
                buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
            }
        }
        return toLeakAwareBuffer(buf);
    }

    获取了directArena对象之后, 通过allocate方法分配一个ByteBuf, 这里allocate方法是PoolArena类中的方法

    跟到allocate方法中:

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

    首先通过newByteBuf获得一个ByteBuf对象

    再通过allocate方法进行分配, 这里要注意, 这里进行分配的时候是线程私有的directArena进行分配

    我们跟到newByteBuf方法中

    因为是directArena调用的newByteBuf, 所以这里会进入DirectArena类的newByteBuf中:

    protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) { 
        if (HAS_UNSAFE) { 
            return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
        } else {
            return PooledDirectByteBuf.newInstance(maxCapacity);
        }
    }

    因为默认通常是有unsafe对象的, 所以这里会走到这一步中PooledUnsafeDirectByteBuf.newInstance(maxCapacity)

    通过静态方法newInstance创建一个PooledUnsafeDirectByteBuf对象

    跟到newInstance方法中:

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

    这里通过RECYCLER.get()这种方式拿到一个ByteBuf对象, RECYCLER其实是一个对象回收站, 这部分内容会在后面的内容中详细剖析, 这里我们只需要知道, 这种方式能从回收站中拿到一个对象, 如果回收站里没有相关对象, 则创建一个新

    因为这里有可能是从回收站中拿出的一个对象, 所以通过reuse进行复用

    跟到reuse方法中:

    final void reuse(int maxCapacity) {
        maxCapacity(maxCapacity);
        setRefCnt(1);
        setIndex0(0, 0);
        discardMarks();
    }

    这里设置了的最大可扩容内存, 对象的引用数量, 读写指针位置都重置为0, 以及读写指针的位置标记也都重置为0

    我们回到PoolArena的allocate方法中:

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

    拿到了ByteBuf对象, 就可以通过allocate(cache, buf, reqCapacity)方法进行内存分配了

    跟到allocate方法中:

    private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        //规格化
        final int normCapacity = normalizeCapacity(reqCapacity);
        if (isTinyOrSmall(normCapacity)) { 
            int tableIdx;
            PoolSubpage<T>[] table;
            //判断是不是tinty
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512
                //缓存分配
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
                    return;
                }
                //通过tinyIdx拿到tableIdx
                tableIdx = tinyIdx(normCapacity);
                //subpage的数组
                table = tinySubpagePools;
            } else {
                if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
                    return;
                }
                tableIdx = smallIdx(normCapacity);
                table = smallSubpagePools;
            }
    
            //拿到对应的节点
            final PoolSubpage<T> head = table[tableIdx];
    
            synchronized (head) {
                final PoolSubpage<T> s = head.next;
                //默认情况下, head的next也是自身
                if (s != head) {
                    assert s.doNotDestroy && s.elemSize == normCapacity;
                    long handle = s.allocate();
                    assert handle >= 0;
                    s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
    
                    if (tiny) {
                        allocationsTiny.increment();
                    } else {
                        allocationsSmall.increment();
                    }
                    return;
                }
            }
            allocateNormal(buf, reqCapacity, normCapacity);
            return;
        }
        if (normCapacity <= chunkSize) {
            //首先在缓存上进行内存分配
            if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
                //分配成功, 返回
                return;
            }
            //分配不成功, 做实际的内存分配
            allocateNormal(buf, reqCapacity, normCapacity);
        } else {
            //大于这个值, 就不在缓存上分配
            allocateHuge(buf, reqCapacity);
        }
    }

    这里看起来逻辑比较长, 其实主要步骤分为两步

    1.首先在缓存上进行分配, 对应步骤是:

      cache.allocateTiny(this, buf, reqCapacity, normCapacity)

      cache.allocateSmall(this, buf, reqCapacity, normCapacity)

      cache.allocateNormal(this, buf, reqCapacity, normCapacity)

    2.如果在缓存上分配不成功, 则实际分配一块内存, 对应步骤是

      allocateNormal(buf, reqCapacity, normCapacity)

    在这里对几种类型的内存进行介绍:

    之前的小节我们介绍过, 缓冲区内存类型分为tiny, small, 和normal, 其实还有种不常见的类型叫做huge, 那么这几种类型的内存有什么区别呢, 实际上这几种类型是按照缓冲区初始化空间的范围进行区分的, 具体区分如下:

    tiny类型对应的缓冲区范围为0-512B

    small类型对应的缓冲区范围为512B-8K

    normal类型对应的缓冲区范围为8K-16MB

    huge类型对应缓冲区范围为大于16MB

    简单介绍下有关范围的含义:

    16MB对应一个chunk, netty是以chunk为单位向操作系统申请内存的

    8k对应一个page, page是将chunk切分后的结果, 一个chunk对应2048个page

    8k以下对应一个subpage, subpage是page的切分, 一个page可以切分多个subpage, 具体切分几个需要根据subpage的大小而定, 比如只要分配1k的缓冲区, 则会将page切分成8个subpage

    以上就是directArena内存分配的大概流程和相关概念

     

    上一节: PooledByteBufAllocator简述

    下一节: 命中缓存的分配

     

  • 相关阅读:
    Fiddler 的几个用法
    jQuery中ajax的使用和缓存问题解决 $getjson 与$get都会被IE缓存
    http与https的区别以及https的加密原理
    asp.net mvc通过预处理实现数据过滤和数据篡改。
    C# winform的WebBrowser非常规编程(强烈推荐)
    DES加密 java与.net可以相互加密解密的方法
    日常开发中常见的HTTP协议的状态码
    前端性能监控
    在手机的浏览器上通过连接打开App
    工作五年总结一二三流公司的常见特性
  • 原文地址:https://www.cnblogs.com/xiangnan6122/p/10205478.html
Copyright © 2011-2022 走看看