Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator

Netty中的内存分配是基于ByteBufAllocator这个接口实现的,通过对它的具体实现,可以用来分配我们之前描述过的任意类型的BytebBuf实例;我们先看一下ByteBufAllocator接口中的定义的关键方法

一、ByteBufAllocator 构造

public interface ByteBufAllocator {

    ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;

    //根据具体实现返回基于直接内存或堆内内存的ByteBuf
    ByteBuf buffer();

    //根据具体实现返回一个给定初始容量的基于直接内存或堆内内存的ByteBuf
    ByteBuf buffer(int initialCapacity);

    //根据具体实现返回一个给定初始容量与最大量的基于直接内存或堆内内存的ByteBuf
    ByteBuf buffer(int initialCapacity, int maxCapacity);

    //返回一个用于套接字操作的ByteBuf
    ByteBuf ioBuffer();

    //返回一个用于套接字操作的给定初始量ByteBuf
    ByteBuf ioBuffer(int initialCapacity);

    //返回一个用于套接字操作的给定初始量与最大量的ByteBuf
    ByteBuf ioBuffer(int initialCapacity, int maxCapacity);

    //返回一个基于堆内内存的ByteBuf
    ByteBuf heapBuffer();

    //返回一个给定初始量基于堆内内存的ByteBuf
    ByteBuf heapBuffer(int initialCapacity);

    //返回一个给定初始量与最大量的基于堆内内存的ByteBuf
    ByteBuf heapBuffer(int initialCapacity, int maxCapacity);

    //返回一个基于直接内存的ByteBuf
    ByteBuf directBuffer();

    //返回一个给定初始量基于直接内存的ByteBuf
    ByteBuf directBuffer(int initialCapacity);

    //返回一个给定初始量与最大量的基于直接内存的ByteBuf
    ByteBuf directBuffer(int initialCapacity, int maxCapacity);

    //返回一个基于复合缓冲区的ByteBuf,基于堆还是直接内存看具体实现
    CompositeByteBuf compositeBuffer();

    //返回一个给定最大量的基于复合缓冲区的ByteBuf,基于堆还是直接内存看具体实现
    CompositeByteBuf compositeBuffer(int maxNumComponents);

    //返回一个基于堆内存的CompositeByteBuf
    CompositeByteBuf compositeHeapBuffer();

    //返回一个给定最大量基于堆内存的CompositeByteBuf
    CompositeByteBuf compositeHeapBuffer(int maxNumComponents);

    //返回一个基于直接内存的CompositeByteBuf
    CompositeByteBuf compositeDirectBuffer();

    //返回一个给定最大量的基于直接内存的CompositeByteBuf
    CompositeByteBuf compositeDirectBuffer(int maxNumComponents);

    //直接内存是否池化管理
    boolean isDirectBufferPooled();

    //计算bytebuf需要扩展时的新容量
    int calculateNewCapacity(int minNewCapacity, int maxCapacity);
 }

可以看到接口中定义的方法基本都是用于分配不同类型的内存,接下来我们看下基于ByteBufAllocator 接口的具体实现类。

看下实现ByteBufAllocator 接口的类结构图

Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator

顶层抽象类AbstractByteBufAllocator下有两大子类PooledByteBufAllocator与UnpooledByteBufAllocator,分别用于池化与非池化内存的构造;

二、ByteBufAllocator 使用

首先我们需要注意下ChannelOption.ALLOCATOR这个配置项,如果不进行特殊配置, 默认为PooledByteBufAllocator,默认ByteBuf类型为PooledUnsafeDirectByteBuf,这里演示需要改为UnpooledByteBufAllocator

b.childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);

在数据入站Handler中,我们通过使用不同的BufAllocator实现类来分配ByteBuf进行对比,主要关注下分配的ByteBuf的类型与是否池化

public class BuffHandler extends ChannelInboundHandlerAdapter{

    PooledByteBufAllocator pdallocator = new PooledByteBufAllocator(true);//池化直接内存

    PooledByteBufAllocator pallocator = new PooledByteBufAllocator(false);//池化堆内存

    UnpooledByteBufAllocator adllocator = new UnpooledByteBufAllocator(true);//非池化直接内存

    UnpooledByteBufAllocator allocator = new UnpooledByteBufAllocator(false);//非池化堆内存

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf outBuffer = (ByteBuf) msg;
        System.err.println("outBuffer is :"+outBuffer.getClass());
        System.err.println("outBuffer's model is :"+outBuffer.isDirect());
        outBuffer = ByteBufAllocator.DEFAULT.buffer();//ByteBufAllocator默认内存类型
        System.err.println("ByteBufAllocator.DEFAULT.buffer() is :"+outBuffer.getClass());
        System.err.println("ByteBufAllocator.DEFAULT.buffer()'s model is :"+outBuffer.isDirect());
        outBuffer = pdallocator.buffer();////池化直接内存
        System.err.println("PooledByteBufAllocator(true) is :"+outBuffer.getClass());
        System.err.println("PooledByteBufAllocator(true)'s model is :"+outBuffer.isDirect());
        outBuffer = pallocator.buffer();//池化队堆内存
        System.err.println("PooledByteBufAllocator(false) is :"+outBuffer.getClass());
        System.err.println("PooledByteBufAllocator(false)'s model is :"+outBuffer.isDirect());
        outBuffer = adllocator.buffer();////非池化直接内存
        System.err.println("UnpooledByteBufAllocator(true) is :"+outBuffer.getClass());
        System.err.println("UnpooledByteBufAllocator(true)'s  model is :"+outBuffer.isDirect());
        outBuffer = allocator.buffer();//非池化堆内存
        System.err.println("UnpooledByteBufAllocator(false) is :"+outBuffer.getClass());
        System.err.println("UnpooledByteBufAllocator(false)'s  model is :"+outBuffer.isDirect());
        byte[] sendBytes = new byte[] {0x7E,0x01,0x02,0x7e};
        outBuffer.writeBytes(sendBytes);
        ctx.writeAndFlush(outBuffer);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.

        cause.printStackTrace();
        ctx.close();
    }
}

根据BufAllocator具体实现类与preferDirect参数会分配不同类型的ByteBuf,输出结果如下:

outBuffer is :class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf
outBuffer's model is :true
ByteBufAllocator.DEFAULT.buffer() is :class io.netty.buffer.PooledUnsafeDirectByteBuf
ByteBufAllocator.DEFAULT.buffer()'s model is :true
PooledByteBufAllocator(true) is :class io.netty.buffer.PooledUnsafeDirectByteBuf
PooledByteBufAllocator(true)'s model is :true
PooledByteBufAllocator(false) is :class io.netty.buffer.PooledUnsafeHeapByteBuf
PooledByteBufAllocator(false)'s model is :false
UnpooledByteBufAllocator(true) is :class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf
UnpooledByteBufAllocator(true)'s  model is :true
UnpooledByteBufAllocator(false) is :class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf
UnpooledByteBufAllocator(false)'s  model is :false

上面示例中,BufAllocator具体实现基本可以分为以下四种,下面我们对四种分配模式的具体实现进行下追踪与分析

new PooledByteBufAllocator(true);//池化直接内存
new PooledByteBufAllocator(false);//池化堆内存
new UnpooledByteBufAllocator(true);//非池化直接内存
new UnpooledByteBufAllocator(false);//非池化堆内存

三、ByteBufAllocator 实现

ByteBufAllocator.DEFAULT.buffer()

这里使用了 ByteBufUtil DEFAULT_ALLOCATOR,我们进入ByteBufUtil类内部看下具体实现

static final ByteBufAllocator DEFAULT_ALLOCATOR;

    static {
        String allocType = SystemPropertyUtil.get(
                "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
        allocType = allocType.toLowerCase(Locale.US).trim();//读取io.netty.allocator.type配置

        //根据配置类型实例化不同类型的BufAllocator实现类
        ByteBufAllocator alloc;
        if ("unpooled".equals(allocType)) {
            //  DEFAULT = new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());
            alloc = UnpooledByteBufAllocator.DEFAULT;
            logger.debug("-Dio.netty.allocator.type: {}", allocType);
        } else if ("pooled".equals(allocType)) {
            // DEFAULT = new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
            alloc = PooledByteBufAllocator.DEFAULT;
            logger.debug("-Dio.netty.allocator.type: {}", allocType);
        } else {
            alloc = PooledByteBufAllocator.DEFAULT;
            logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
        }

        DEFAULT_ALLOCATOR = alloc;

        THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 0);
        logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);

        MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.maxThreadLocalCharBufferSize", 16 * 1024);
        logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
    }

ByteBufUtil 的静态构造中会根据io.netty.allocator.type配置的不同实例化不同类型的BufAllocator实现类,下面我们看下BufAllocator的两个具体实现类PooledByteBufAllocator与UnpooledByteBufAllocator

PooledByteBufAllocator

在PooledByteBufAllocator构造函数中,首先会进行内存池的详细配置

    public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
                                  int tinyCacheSize, int smallCacheSize, int normalCacheSize,
                                  boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
        super(preferDirect);
        //声明一个PoolThreadLocalCache用于内存申请
        threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
        this.tinyCacheSize = tinyCacheSize;
        this.smallCacheSize = smallCacheSize;
        this.normalCacheSize = normalCacheSize;
        chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);

        checkPositiveOrZero(nHeapArena, "nHeapArena");
        checkPositiveOrZero(nDirectArena, "nDirectArena");

        checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");
        if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
            throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
        }

        if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
            throw new IllegalArgumentException("directMemoryCacheAlignment: "
                    + directMemoryCacheAlignment + " (expected: power of two)");
        }

        int pageShifts = validateAndCalculatePageShifts(pageSize);

        if (nHeapArena > 0) {
            heapArenas = newArenaArray(nHeapArena);
            List metrics = new ArrayList(heapArenas.length);
            for (int i = 0; i < heapArenas.length; i ++) {
                PoolArena.HeapArena arena = new PoolArena.HeapArena(this,
                        pageSize, maxOrder, pageShifts, chunkSize,
                        directMemoryCacheAlignment);
                heapArenas[i] = arena;
                metrics.add(arena);
            }
            heapArenaMetrics = Collections.unmodifiableList(metrics);
        } else {
            heapArenas = null;
            heapArenaMetrics = Collections.emptyList();
        }

        if (nDirectArena > 0) {
            directArenas = newArenaArray(nDirectArena);
            List metrics = new ArrayList(directArenas.length);
            for (int i = 0; i < directArenas.length; i ++) {
                PoolArena.DirectArena arena = new PoolArena.DirectArena(
                        this, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
                directArenas[i] = arena;
                metrics.add(arena);
            }
            directArenaMetrics = Collections.unmodifiableList(metrics);
        } else {
            directArenas = null;
            directArenaMetrics = Collections.emptyList();
        }
        metric = new PooledByteBufAllocatorMetric(this);
    }

紧接着看下PooledByteBufAllocator具体的内存分配方法

newDirectBuffer 分配直接内存
@Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena directArena = cache.directArena;//具体的内存分配类PoolArena

        final ByteBuf buf;
        if (directArena != null) {//PoolArena就是Netty的内存池实现类。实现具体内存分配
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            buf = PlatformDependent.hasUnsafe() ?
                    UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
                    new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }
heapBuffer 分配堆内内存
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<byte[]> heapArena = cache.heapArena;

        final ByteBuf buf;
        if (heapArena != null) {  //通过PoolArena分配堆内内存
            buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            buf = PlatformDependent.hasUnsafe() ?
                    new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
                    new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }

UnpooledByteBufAllocator

由于采用非池化管理,UnpooledByteBufAllocator构造函数中需要指定内存清理策略

public UnpooledByteBufAllocator(boolean preferDirect, boolean disableLeakDetector, boolean tryNoCleaner) {
        super(preferDirect);
        this.disableLeakDetector = disableLeakDetector;
        //内存清理策略,默认noCleaner需要使用 unSafe 的 freeMemory 方法释放内存
        //noCleaner 使用 unSafe.freeMemory(address);
        //hasCleaner 使用 DirectByteBuffer 的 Cleaner 的 clean 方法。
        noCleaner = tryNoCleaner && PlatformDependent.hasUnsafe()
                && PlatformDependent.hasDirectBufferNoCleanerConstructor();
    }

紧接着看下UnpooledByteBufAllocator具体的内存分配方法

newDirectBuffer 分配直接内存
@Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        final ByteBuf buf;
        if (PlatformDependent.hasUnsafe()) {//判断是否适用Unsafe操作
            buf = noCleaner ? new InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf(this, initialCapacity, maxCapacity) :
                    new InstrumentedUnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            buf = new InstrumentedUnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }
        return disableLeakDetector ? buf : toLeakAwareBuffer(buf);//是否进行堆外内存泄露监控
    }
newHeapBuffer 分配堆内内存
@Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return PlatformDependent.hasUnsafe() ?
                new InstrumentedUnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
                new InstrumentedUnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

四、总结

通过以上内容我们梳理了Netty中BufAllocator的具体实现及分配内存的类型,从内存管理模式上分为池化与非池化,从内存分配类型上分为直接内存与堆内内存,本文我们只是初步对其进行了总结,Netty分配内存的具体实现及精巧设计都还未涉及,后续我们会继续对其进行进一步的探究,希望本文对大家能有所帮助,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator

Original: https://www.cnblogs.com/dafanjoy/p/13687834.html
Author: DaFanJoy
Title: Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/593433/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • WWDC2016-session402-whatsNewInSwift3

    Dock 应用的介绍:1.设计到的东西多2.使用 swift 设计3.Dock 的代码量: 200,000行4.更少的重写相同功能的代码 swift.org 官网介绍 Swift …

    Java 2023年5月30日
    076
  • 获取字符串中倒数第二个特定字符的位置

    开发场景中,有时候会遇到取文件路径中的 文件夹名称,这些文件路径往往是 http 格式的,比如下面这样: https://img2020.cnblogs.com/blog/2413…

    Java 2023年6月6日
    0106
  • springboot 整合 Shiro 配置类

    ckage org.fh.config; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apac…

    Java 2023年6月8日
    077
  • Java 实现等频分箱

    等频离散法 Python 实现方式 Java 实现方式 测试结果对比 总结 等频离散法 根据数据的频率分布进行排序,然后按照频率进行离散,好处是数据变为均匀分布,但是会更改原有的数…

    Java 2023年6月9日
    0114
  • MybatisPlus——全网配置最全的代码生成器

    MybatisPlus代码生成器 这里讲解的是新版 (mybatis-plus 3.5.1+版本),旧版不兼容 官方文档:https://baomidou.com/(建议多看看官方…

    Java 2023年6月14日
    072
  • 云原生的概念

    云原生其实是一种思想,并不是一种工具,云原生更多的是一种泛化的东西,是一种思想观念,首先要有意识的去想云原生这种东西,其次,他是一种技术、流程和企业管理方法的集合,所谓的技术,k8…

    Java 2023年6月8日
    079
  • Linux基础入门笔记

    Linux内核最初只是由芬兰人林纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。 Linux是一套免费使用和自由传播的类Unix操作系统,是一…

    Java 2023年6月7日
    057
  • org.activiti.engine.ActivitiException: Could not update Activiti database schema: unknown version from database: ‘7.1.0-M6’

    原因: Activiti 相关的jar 版本和表act_ge_property 中schema.version 所存储的版本不一致造成的 查看activiti 相关jar 包版本修…

    Java 2023年6月16日
    068
  • 配置SpringBoot方便的切换jar和war

    网上关于如何切换,其实说的很明确,本文主要通过profile进行快速切换已实现在不同场合下,用不同的打包方式。 jar到war修改步骤 pom文件修改 packaging配置由ja…

    Java 2023年5月30日
    078
  • 设计模式——结构性设计模式

    结构性设计模式 针对类与对象的组织结构。(白话:类与对象之间的交互的多种模式 类/对象适配器模式 当需要传入一个A类型参数,但只有B类型类时,就需要一个A类型的适配器装入B类的数据…

    Java 2023年6月14日
    072
  • Java Date 和 Calendar 实例

    Java 之 Date 和 Calendar 实例 package com.homer.learn; import java.text.DateFormat; import jav…

    Java 2023年5月29日
    065
  • Redis的五种基本数据类型

    Redis的五种基本数据类型 1、概述 Redis是一个由C语言开发的基于key-value形式的非关系型数据库 key-value:键值对【键:String,值:五种数据类型】 …

    Java 2023年6月8日
    088
  • RabbitMQ部署指南

    RabbitMQ部署指南 1.单机部署 我们在Centos7虚拟机中使用Docker来安装。 1.1.下载镜像 方式一:在线拉取 docker pull rabbitmq:3.8-…

    Java 2023年6月13日
    072
  • 设计模式之责任链模式

    责任链模式又称职责链模式,属于行为型模式;在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请…

    Java 2023年6月5日
    099
  • Webpack的使用

    全局安装Webpack(尽量在项目目录中执行cmd命令) npm install -g webpack webpack-cli 在项目目录的js目录中创建几个js文件(之间的关联任…

    Java 2023年6月13日
    086
  • 教学日志:javaSE-面向对象1

    对象,类,属性,方法的理解 package com.tengxun.class6.oop1; /** * @Auther: Yu Panpan * @Date: 2021/12/7…

    Java 2023年6月5日
    0101
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球