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. MySQL主主模式+Keepalived高可用

    今天闲来无事,打算搭建一个MySQL的高可用架构,采用的是MySQL的主主结构,再外加Keepalived,对外统一提供虚IP.先来说说背景吧,现在的项目为了高可用性,都是避免单节点的存在的,比如,我 ...

  2. Linux下的django项目01

    1.初始化项目结构 └─shiyanlou_project # 项目根路径 │ .gitignore     # 提交git仓库时,不提交的文件必须要在这里进行标注 │ README.en.md # ...

  3. Android Studio的第一次经历

    第一个简单APP的制作是从xml开始的,通过在java新建一个empty  activity,并在layout里找到对应的xml文件进行编写.每编写一个xml就要事先新建 一个对应的empty  ac ...

  4. 云服务器部署scrapyd爬虫

    Scrapyd部署爬虫项目 GitHub:https://github.com/scrapy/scrapyd API 文档:http://scrapyd.readthedocs.io/en/stabl ...

  5. LRU算法详解

    一.什么是 LRU 算法 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置.但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留 ...

  6. OJ-1:时钟问题【九度1553】

    题目描述: 如图,给定任意时刻,求时针和分针的夹角(劣弧所对应的角). 输入: 输入包含多组测试数据,每组测试数据由一个按hh:mm表示的时刻组成. 输出: 对于每组测试数据,输出一个浮点数,代表时针 ...

  7. knn-demo

    from __future__ import print_functionfrom numpy import *# 导入科学计算包numpy和运算符模块operatorimport operatorf ...

  8. 主动关闭 tcp_timewait_state_process 处理

    正常情况下主动关闭连接的一端在连接正常终止后,会进入TIME_WAIT状态,存在这个状态有以下两个原因(参考<Unix网络编程>):      1.保证TCP连接关闭的可靠性.如果最终发送 ...

  9. dp背包 面试题 08.11. 硬币

    https://leetcode-cn.com/problems/coin-lcci/ 硬币.给定数量不限的硬币,币值为25分.10分.5分和1分,编写代码计算n分有几种表示法.(结果可能会很大,你需 ...

  10. 双数组字典树(Double Array Trie)

    参考文献 1.双数组字典树(DATrie)详解及实现 2.小白详解Trie树 3.论文<基于双数组Trie树算法的字典改进和实现> DAT的基本内容介绍这里就不展开说了,从Trie过来的同 ...