netty(七)buffer源码学习2

概述

文章主要介绍的是PoolArena,PoolChunk,PoolSubpage 三个类的源码

PoolArena

PoolArena 是netty 的内存池实现类,通过预先申请一块大的空间,然后对空间进行分配和回收,这样就不用频繁进行系统调用,提高性能。
PoolArena 由多个 chunk 组成,chunk 则由多个page 组成.

PoolChunk

Chunk 主要用来组织和管理多个Page的内存分配和释放,在netty中,Chunk 中的Page被构造成一棵二叉树。

PoolSubpage

PoolSubpage保存long[] 数组表示占用情况。数组中一个long元素最多表示64分空间的占用情况,假如某块空间分的分数多于64份,那么数组中加多一个元素,例如4字节分4份,long元素的第四位就可以表示完,要是128字节分128份,long数组就要添加多一个元素共2个元素,一个long表示64份。

内存回收策略

  • chunk 回收 : 二叉树节点状态标识符
  • page 回收 : 维护块的使用状态来标识

源码

上面就是我们刚才讲的几个类的类结构。
final class PoolSubpage<T> {

	//保持一个 PoolChunk
    final PoolChunk<T> chunk;
    final int memoryMapIdx;
    final int runOffset;
    final int pageSize;
    final long[] bitmap;

    //前后指针
    PoolSubpage<T> prev;
    PoolSubpage<T> next;

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

    // TODO: Test if adding padding helps under contention
    //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;

    /** Special constructor that creates a linked list head */
    PoolSubpage(int pageSize) {
        chunk = null;
        memoryMapIdx = -1;
        runOffset = -1;
        elemSize = -1;
        this.pageSize = pageSize;
        bitmap = null;
    }

    ...
final class PoolChunkList<T> {
    private final PoolArena<T> arena;
    //前后指针
    private final PoolChunkList<T> nextList;
    PoolChunkList<T> prevList;

    ...
 }
abstract class PoolArena<T> {

    final PooledByteBufAllocator parent;

    private final int pageSize;
    private final int maxOrder;
    private final int pageShifts;
    private final int chunkSize;
    private final int subpageOverflowMask;

    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;

    // TODO: Test if adding padding helps under contention
    //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;

    protected PoolArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize) {
        this.parent = parent;
        this.pageSize = pageSize;
        this.maxOrder = maxOrder;
        this.pageShifts = pageShifts;
        this.chunkSize = chunkSize;
        subpageOverflowMask = ~(pageSize - 1);

        tinySubpagePools = newSubpagePoolArray(512 >>> 4);
        for (int i = 0; i < tinySubpagePools.length; i ++) {
            tinySubpagePools[i] = newSubpagePoolHead(pageSize);
        }

        smallSubpagePools = newSubpagePoolArray(pageShifts - 9);
        for (int i = 0; i < smallSubpagePools.length; i ++) {
            smallSubpagePools[i] = newSubpagePoolHead(pageSize);
        }

        q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE);
        q075 = new PoolChunkList<T>(this, q100, 75, 100);
        q050 = new PoolChunkList<T>(this, q075, 50, 100);
        q025 = new PoolChunkList<T>(this, q050, 25, 75);
        q000 = new PoolChunkList<T>(this, q025, 1, 50);
        qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25);

        q100.prevList = q075;
        q075.prevList = q050;
        q050.prevList = q025;
        q025.prevList = q000;
        q000.prevList = null;
        qInit.prevList = qInit;
    }

    ....

源码分析 PooledDirectByteBuf

开始看 PooledDirectByteBuf 的方法有点乱,我们可以从调用它的那个类开始分析。 PooledByteBufAllocator内部就是可以调用 PooledDirectByteBuf 生成 ByteBuf的类。它的使用如下 :

    public static void main(String[] args){
        /*
         * 内存池buffer 提供为外部的使用主要是通过 PooledByteBufAllocator 这个类堆外提供
         */
        PooledByteBufAllocator allocator = new PooledByteBufAllocator();
        ByteBuf directBuffer = allocator.directBuffer();
    }
下面我们看一下 directBuffer 方法。
    @Override
    public ByteBuf directBuffer() {
        return directBuffer(DEFAULT_INITIAL_CAPACITY, Integer.MAX_VALUE);
    }

    @Override
    public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        //校验
        validate(initialCapacity, maxCapacity);
        //子类实现,申请空间
        return newDirectBuffer(initialCapacity, maxCapacity);
    }

    // PooledByteBufAllocator 类中的实现
    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        //No.1 缓冲中获取
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            //假如缓冲中的 PoolArena 不存在
            if (PlatformDependent.hasUnsafe()) {
                buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
            } else {
                buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
            }
        }

        return toLeakAwareBuffer(buf);
    }

    // PooledByteBufAllocator 类中的threadCache ,是个ThreadLocal
    final ThreadLocal<PoolThreadCache> threadCache = new ThreadLocal<PoolThreadCache>() {
        private final AtomicInteger index = new AtomicInteger();
        @Override
        protected PoolThreadCache initialValue() {
            final int idx = index.getAndIncrement();
            final PoolArena<byte[]> heapArena;
            final PoolArena<ByteBuffer> directArena;

            if (heapArenas != null) {
                heapArena = heapArenas[Math.abs(idx % heapArenas.length)];
            } else {
                heapArena = null;
            }

            if (directArenas != null) {
                directArena = directArenas[Math.abs(idx % directArenas.length)];
            } else {
                directArena = null;
            }

            return new PoolThreadCache(heapArena, directArena);
        }
    };

参考资料

  • 《netty权威指南》

原文地址:https://www.cnblogs.com/Benjious/p/11634891.html

时间: 2024-11-09 10:02:14

netty(七)buffer源码学习2的相关文章

netty(八)buffer源码学习3

问题 : compositeByteBuf 是干什么和其他 compositeByteBuf 有何区别 内部实现 概述 compositeByteBuf 就像数据库中的视图,把几个表的字段组合在一起,它的应用场景比如一个自定义协议有消息头和消息体,而两者是分开到两个 ByteBuf 的,那么这时候要怎么把两个ByteBuf 放到一起管理呢?compositeByteBuf就可以解决这个问题. 源码 compositeByteBuf 内部放着一个 Components 的数组,这个 Compone

netty(六) buffer 源码分析

问题 : netty的 ByteBuff 和传统的ByteBuff的区别是什么? HeapByteBuf 和 DirectByteBuf 的区别 ? HeapByteBuf : 使用堆内存,缺点 ,socket 传输的时候由于需要复制的原因,慢一点 DirectByteBuf : 堆外内存,可以使用零拷贝 概述 netty ByteBuf 存在两个指针,分成三个区域: 已读区(可丢弃),未读区(未读),可写区 .不像之前JDK 的 ByteBuffer 中只有一个position 指针.例如以下

Netty源码学习——Included transports(传输方式)

Transport API的核心: Channel接口 类图表示Channel含有Pipeline和Config接口,pipeline上一节有所介绍. Channel是线程安全的,这表示在多线环境下操作同一个Channel,不会有数据问题. final Channel channel = null; final ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8); // #1 Runnable writ

Netty源码学习——ChannelPipeline模型分析

参考Netty API io.netty.channel.ChannelPipeline A list of ChannelHandlers which handles or intercepts inbound events and outbount operations of aChannel.ChannelPipeline implements an advanced form of theIntercepting Filter pattern to give a user full co

Netty源码学习——EventLoopGroup原理:NioEventLoopGroup分析

类结构图: 不了解Executor接口原理的可以查看concurrent包中的api介绍,这里只介绍Netty中EventExecutorGroup的主要功能! 从类的结构图中可以看到EventExecutorGroup是直接继承ScheduledExecutorService这个接口的,为了说明白Group的原理这里顺便提一下ScheduledExecutorService的用途! java.util.concurrent.ScheduledExecutorService An Executo

jQuery源码学习笔记五 六 七 八 转

jQuery源码学习笔记五 六 七 八 转 Js代码   <p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等.接着是其缓存机制data.</p> <pre class="brush:javascript;gutter:false;toolbar:false"> //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/ru

Redis源码学习-Lua脚本

Redis源码学习-Lua脚本 1.Sublime Text配置 我是在Win7下,用Sublime Text + Cygwin开发的,配置方法请参考<Sublime Text 3下C/C++开发环境搭建>. 要注意的是:在Cygwin中安装Lua解析器后,SublimeClang插件就能识别出可饮用的Lua头文件了,因为Build System中我们已经配置过"-I", "D:\\cygwin64\\usr\\include",而新安装的Lua头文件会

Spring源码学习笔记(6)

Spring源码学习笔记(六) 前言-- 最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. 上一篇中我们梳理到 Spring 加载 XML 配置文件, 完成 XML 的解析工作,接下来我们将进入 Spring 加载 bean 的逻辑. 我们使用 Spring 获取 XML

Spring源码学习笔记(3)

Spring源码学习笔记(三) 前言----     最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. DispatcherServlet 实现核心功能 和普通的 Servelt 类一样, DispatcherServlet 中的 doGet() 和 doPost() 方法