Netty Recycler的源码分析
Recycler分析
调用来源:PooledByteBuf.java
涉及的知识:
- AtomicInteger
- WeakReference
- ThreadLocal
在DefaultHandle 中调用的recycle只是把需要回收的对象装在一个栈中,那么问题转化为如何这个栈是如何回收的.
static final class DefaultHandle<T> implements Handle<T> {
private int lastRecycledId;
private int recycleId;
boolean hasBeenRecycled;
private Stack<?> stack;
private Object value;
DefaultHandle(Stack<?> stack) {
this.stack = stack;
}
//其他方法调用的初始入口
@Override
public void recycle(Object object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
stack.push(this);
}
}
那么回收的方法应该和stack有关.
在介绍栈之前先了解下WeakOrderQueue,顾名思义,它是弱有序队列.
WeakOrderQueue是由链表实现,其中存储的数据是DefaultHanle类型的数组.值得注意的是虽然类名由弱引用字样,但是属性中并没有弱引用字段.
该类中的所有构造方法都是private类型的,获取实例只能通过newQueue方法获取:
private WeakOrderQueue(Stack<?> stack, Thread thread) {
head = tail = new Link();
owner = new WeakReference<Thread>(thread);
// Its important that we not store the Stack itself in the WeakOrderQueue as the Stack also is used in
// the WeakHashMap as key. So just store the enclosed AtomicInteger which should allow to have the
// Stack itself GCed.
availableSharedCapacity = stack.availableSharedCapacity;
}
static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
WeakOrderQueue queue = new WeakOrderQueue(stack, thread);
// Done outside of the constructor to ensure WeakOrderQueue.this does not escape the constructor and so
// may be accessed while its still constructed.
stack.setHead(queue);
return queue;
}
可以看到该方法除了构造并返回WeakOrderQueue实例外还将该实例放入了一个Stack实例中的容器中.
该容器在类中的属性是head,数据结构是用链表实现的栈.(原因)
其中WeakOrderQueue包含一个Link内部类,该内部类继承了AtomicInteger,含有一个存放DefaultHandle类型的数组属性.继承的AtomicInteger用来记录数组的下标.如代码:
private static final class Link extends AtomicInteger {
private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];
private int readIndex;
private Link next;
}
其中Link的作用我认为是空间分配与释放的基本单位,当一个Link元素填满时,需要重新生成一个Link实例并将其置入链表末尾.释放的时候在transfer方法中释放.
transfer方法的作用:当Stack没有元素可以提供给消费者时,transfer方法将WeakOrderQueue中的元素传送给该Stack.该行为由Stack主动发起,所以在Stack中才会有一个WeakOrderQueue的链表,Stack在这些链表元素里获取消费者需要的数据.而当一个Link完全被转化后它的引用会被撤销,从而等待被GC
Stack之中的存放的元素是DefaultHandle类型的实例,并且存有一个head属性存放WeakOrderQueue.
Stack是外界唯一可访问Recycler类中存储的handle元素.当自身实例不足时,会主动发起transfer方法从WeakOrderQueue中获取.消费者获取元素的代码如下:
//类Recycler
public final T get() {
if (maxCapacityPerThread == 0) {
return newObject((Handle<T>) NOOP_HANDLE);
}
Stack<T> stack = threadLocal.get();
DefaultHandle<T> handle = stack.pop();
if (handle == null) {
handle = stack.newHandle();
handle.value = newObject(handle);
}
return (T) handle.value;
}
//内部类Stack
DefaultHandle<T> pop() {
int size = this.size;
if (size == 0) {
if (!scavenge()) {
return null;
}
size = this.size;
}
size --;
DefaultHandle ret = elements[size];
elements[size] = null;
if (ret.lastRecycledId != ret.recycleId) {
throw new IllegalStateException("recycled multiple times");
}
ret.recycleId = 0;bei
ret.lastRecycledId = 0;
this.size = size;
return ret;
}
其中:
- 如果stack所代指的池中没有实例,则会new一个Object返回.
- threadLocal解决了线程同步的问题,不过netty并没有使用JDK自带的ThreadLocal,而是自己定义了一个叫FastThreadLocal的类实现的,感兴趣的可以看看源码.
- 每次成功pop一个实例就会将它的recycleId和lastRecycledId置为0,保证下回可再次回收.
注意这行代码的意义
if (ret.lastRecycledId != ret.recycleId) {
throw new IllegalStateException("recycled multiple times");
}
我认为是这样的:Stack返回给消费者的实例必然是没有其他消费者在使用的实例,也就是说在Stack中的实例都是没有其他消费者使用的.而这两个值什么时候不相等呢,如果一个实例本身在Stack中,已经回收了,在这种状态下再次被回收后就会出现两值不相等的情况.为什么避免这种情况呢,那是因为这样同一个实例的引用会被放在Stack或者WeakOrderQueue的两个位置,当一个消费者拿走一个引用时,另一个引用所指向的实例已经不在符合'没有人使用'这条规则,所以要避免这种情况.
Stack的push方法:pushNow 和 pushLater
void push(DefaultHandle<?> item) {
Thread currentThread = Thread.currentThread();
if (threadRef.get() == currentThread) {
// The current Thread is the thread that belongs to the Stack, we can try to push the object now.
pushNow(item);
} else {
// The current Thread is not the one that belongs to the Stack
// (or the Thread that belonged to the Stack was collected already), we need to signal that the push
// happens later.
pushLater(item, currentThread);
}
}
简单地说pushNow是将回收元素放在Stack的元素列表中;pushLater则是放在WeakOrderQueue的元素列表中.
存放在哪取决于Stack中的线程引用是否和当前线程相同.于是这里会看出一个优先级,Stack更趋向于给自己引用线程提供可利用元素.
借用网上一张图总结一下:
- 消费者调用Recyler的get方法,即调用Stack的pop方法获取池中元素
- 如果Stack中元素不足,调用transfer方法,触发scavenge方法,然后从Link中的WeakOrderQueue中获取元素.放到Stack中.
- 调用Handle的recycle方法进行回收,即调用Stack的push方法将回收元素放在相应位置.Recycler中页有recycle方法,但是已经被@Deprecated.

Netty Recycler的源码分析的更多相关文章
- 《深入探索Netty原理及源码分析》文集小结
<深入探索Netty原理及源码分析>文集小结 https://www.jianshu.com/p/239a196152de
- Netty 核心组件 Pipeline 源码分析(二)一个请求的 pipeline 之旅
目录大纲: 前言 针对 Netty 例子源码做了哪些修改? 看 pipeline 是如何将数据送到自定义 handler 的 看 pipeline 是如何将数据从自定义 handler 送出的 总结 ...
- Netty中FastThreadLocal源码分析
Netty中使用FastThreadLocal替代JDK中的ThreadLocal[JAVA]ThreadLocal源码分析,其用法和ThreadLocal 一样,只不过从名字FastThreadLo ...
- netty(六) buffer 源码分析
问题 : netty的 ByteBuff 和传统的ByteBuff的区别是什么? HeapByteBuf 和 DirectByteBuf 的区别 ? HeapByteBuf : 使用堆内存,缺点 ,s ...
- 【Netty之旅四】你一定看得懂的Netty客户端启动源码分析!
前言 前面小飞已经讲解了NIO和Netty服务端启动,这一讲是Client的启动过程. 源码系列的文章依旧还是遵循大白话+画图的风格来讲解,本文Netty源码及以后的文章版本都基于:4.1.22.Fi ...
- Netty 5.0源码分析-ByteBuf
1. 概念 Java NIO API自带的缓冲区类功能相当有限,没有经过优化,使用JDK的ByteBuffer操作更复杂.故而Netty的作者Trustin Lee为了实现高效率的网络传输,重新造轮子 ...
- Netty 5.0源码分析之综述
1. 前言 本系列主要是用于梳理Netty的架构流程,深入设计细节,重点关注Netty是如何实现它所声称的特性. (ps:本人水平有限,如有错误,请不吝指教 : )) 2. 什么是Netty Nett ...
- netty之EventLoop源码分析
我们在讲解服务端和客户端时经常会看到提交一个任务到channel对应的EventLoop上,后续的io事件监听和任务执行都在EventLoop完成,可以说EventLoop是netty最核心的组件,我 ...
- Netty 5.0源码分析-Bootstrap
1. 前言 io.netty.bootstrap类包提供包含丰富API的帮助类,能够非常方便的实现典型的服务器端和客户端通道初始化功能. 包含的接口类: //提供工厂类的newChannel方法创建一 ...
随机推荐
- 027. Python面向对象的__init__方法
__init__魔术方法(构造方法) 触发时机:实例化对象,初始化的时候触发 功能:为对象添加成员 参数:参数不固定,至少一个self参数 返回值:无 基本用法,至少含有一个参数 class MyCl ...
- 实操笔记:为 NSQ 配置监控服务的心路历程
在 Go 语言实现的实时消息队列中, NSQ 的热度可以排第一. NSQ 这款消息中间件简单易用,其设计目标是为在分布式环境下运行,为去中心化服务提供一个强大的基础架构.它具有分布式.去中心化的拓扑结 ...
- 重新整理 .net core 实践篇————依赖注入应用之援军[四]
前言 介绍第三方依赖注入框架Autofac,看看为我们解决什么问题. 下面介绍4个点: 命名注册 属性注册 aop 注入 子容器命名 正文 为什么我们需要使用第三方框架?第三方框架为我们做了什么?第三 ...
- Linux 中/var/spool/postfix/maildrop目录下堆积大量小文件 如何删除
Linux 中/var/spool/postfix/maildrop目录下堆积大量小文件 如何删除 1.先删除maildrop目录下的通知邮件文件 命令:find /var/spool/postf ...
- App自动化测试之Appium环境安装(涉及雷电模拟器和真机)
1.安装Microsoft .NET Framework 4.5 及以上版本 2.安装Appium 官方网站地址:http://appium.io/ 我装了1.17.0版本 3.安装JDK 1.8及以 ...
- JVM学习心得—JVM内存模型(个人整理,请勿转载)
一.运行时数据区域 线程私有的:程序计数器+虚拟机栈+本地方法栈 线程共享的:堆+方法区(运行时常量池)+直接内存(非运行时数据区的一部分) *JDK1.8后将方法区废除,新增元空间. 1.1 程序计 ...
- TVM交叉编译和远程RPC
TVM交叉编译和远程RPC 本文介绍了TVM中使用RPC的交叉编译和远程设备执行. 使用交叉编译和RPC,可以在本地计算机上编译程序,然后在远程设备上运行它.当远程设备资源受到限制时(如Raspber ...
- MinkowskiPooling池化(下)
MinkowskiPooling池化(下) MinkowskiPoolingTranspose class MinkowskiEngine.MinkowskiPoolingTranspose(kern ...
- Fine-Tuning微调原理
Fine-Tuning微调原理 如何在只有60000张图片的Fashion-MNIST训练数据集中训练模型.ImageNet,这是学术界使用最广泛的大型图像数据集,它拥有1000多万幅图像和1000多 ...
- 三色标记法与读写屏障, G1工作过程
https://www.jianshu.com/p/12544c0ad5c1 https://www.cnblogs.com/GrimMjx/p/12234564.html 自我总结和记忆: 为了解决 ...