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 接口的类结构图

顶层抽象类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<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(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<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(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<ByteBuffer> 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的更多相关文章

  1. Netty 源码分析系列(二)Netty 架构设计

    前言 上一篇文章,我们对 Netty做了一个基本的概述,知道什么是Netty以及Netty的简单应用. Netty 源码分析系列(一)Netty 概述 本篇文章我们就来说说Netty的架构设计,解密高 ...

  2. Netty源码分析 (十二)----- 心跳服务之 IdleStateHandler 源码分析

    什么是心跳机制? 心跳说的是在客户端和服务端在互相建立ESTABLISH状态的时候,如何通过发送一个最简单的包来保持连接的存活,还有监控另一边服务的可用性等. 心跳包的作用 保活Q:为什么说心跳机制能 ...

  3. Netty源码分析之ByteBuf引用计数

    引用计数是一种常用的内存管理机制,是指将资源的被引用次数保存起来,当被引用次数变为零时就将其释放的过程.Netty在4.x版本开始使用引用计数机制进行部分对象的管理,其实现思路并不是特别复杂,它主要涉 ...

  4. Netty源码分析之NioEventLoop(二)—NioEventLoop的启动

    上篇文章中我们对Netty中NioEventLoop创建流程与源码进行了跟踪分析.本篇文章中我们接着分析NioEventLoop的启动流程: Netty中会在服务端启动和新连接接入时通过chooser ...

  5. Netty源码分析之ChannelPipeline(二)—ChannelHandler的添加与删除

    上篇文章中,我们对Netty中ChannelPipeline的构造与初始化进行了分析与总结,本篇文章我们将对ChannelHandler的添加与删除操作进行具体的的代码分析: 一.ChannelHan ...

  6. [编织消息框架][netty源码分析]11 ByteBuf 实现类UnpooledHeapByteBuf职责与实现

    每种ByteBuf都有相应的分配器ByteBufAllocator,类似工厂模式.我们先学习UnpooledHeapByteBuf与其对应的分配器UnpooledByteBufAllocator 如何 ...

  7. [编织消息框架][netty源码分析]10 ByteBuf 与 ByteBuffer

    因为jdk ByteBuffer使用起来很麻烦,所以netty研发出ByteBuf对象维护管理内存使用ByteBuf有几个概念需要知道1.向ByteBuf提取数据时readerIndex记录最后读取坐 ...

  8. Netty源码分析之ByteBuf(一)—ByteBuf中API及类型概述

    ByteBuf是Netty中主要的数据容器与操作工具,也是Netty内存管理优化的具体实现,本章我们先从整体上对ByteBuf进行一个概述: AbstractByteBuf是整个ByteBuf的框架类 ...

  9. [编织消息框架][netty源码分析]13 ByteBuf 实现类CompositeByteBuf职责与实现

    public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable<ByteBuf ...

随机推荐

  1. .NET CORE WebAPI JWT身份验证

    一.appsettings.Json文件配置 配置JWT公用参数. 1 /*JWT设置*/ 2 "JwtSetting": { 3 "Issuer": &quo ...

  2. python自动化测试,读取excal数据报"'str' object has no attribute 'items'"问题解决

    通过python进行自动化测试,为了方便,对代码和数据进行了分离,此处把测试数据放到了excal表格中.requests.post请求时报"'str' object has no attri ...

  3. Luogu P2536 [AHOI2005]病毒检测

    题意 给一个有通配符的模式串和 \(n\) 个文本串,其中 ? 可以匹配任意字符,* 可以匹配 \(0\) 或任意多个字符,求 \(n\) 个文本串中无法与模式串匹配的数量. \(\texttt{Da ...

  4. ArrayList扩容机制

    一.先从 ArrayList 的构造函数说起 ArrayList有三种方式来初始化,构造方法源码如下: 1 /** 2 * 默认初始容量大小 3 */ 4 private static final i ...

  5. Elasticsearch数据库 | Elasticsearch-7.5.0应用搭建实战

    Elasticsearch 是一个可用于分布式以及符合RESTful 风格的搜索和数据分析引擎.-- Elastic Stack 官网 搭建Elasticsearch的"那些事儿" ...

  6. css 两边宽度固定中间自适应宽度

    #content_left{   //左 width:73px; height:100px; vertical-align:middle; margin-right:-100px; float:lef ...

  7. 【软件配置】JDK+AndroidStudio4.1开发安卓APP环境安装和配置教程详细

    目录 一.专业名词 二.搭建前资源准备 2.1 JDK资源下载 2.2 AndroidStudio下载 三.安装 3.1 JDK安装配置 3.2 AndroidStudio安装 四.创建安卓APP工程 ...

  8. 依赖注入DI(IOC)容器快速入门

    1.什么是IOC IOC是一种设计模式,全程控制翻转或叫依赖注入.更详细介绍见http://martinfowler.com/articles/injection.html 2.为什么用IOC 我们通 ...

  9. 写博客真的很枯燥,更麻烦的是我还不会MD,排版太不友好了啊。

    学习MD 第一标题 #一号标题 ##二号标题 神马啊,博客园居然不支持MD语法

  10. SpringBoot中BeanValidation数据校验与优雅处理详解

    目录 本篇要点 后端参数校验的必要性 不使用Validator的参数处理逻辑 Validator框架提供的便利 SpringBoot自动配置ValidationAutoConfiguration Va ...