Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)—->第3节: recycler的使用和创建
Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler
第三节: recycler的使用和创建
** **
这一小节开始学习
recycler相关的知识
, recycler是
netty实现的一个轻量级对象回收站
, 在
netty中
, recycler的使用也是相当之频繁的
recycler
作用是保证了对象的循环利用
, 对象使用完可以通过
recycler回收
, 需要再次使用则从对象池中取出
, 不用每次都创建新对象从而减少对系统资源的占用
, 同时也减轻了
gc的压力
这里看一个示例
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 1public class RecyclerDemo {
2 private static final Recycler<User> RECYCLER = new Recycler<User>() {
3 @Override
4 protected User newObject(Handle<User> handle) {
5 return new User(handle);
6 }
7 };
8 static class User{
9 private final Recycler.Handle<User> handle;
10 public User(Recycler.Handle<User> handle){
11 this.handle=handle;
12 }
13 public void recycle(){
14 handle.recycle(this);
15 }
16 }
17 public static void main(String[] args){
18 User user1 = RECYCLER.get();
19 user1.recycle();
20 User user2 = RECYCLER.get();
21 user2.recycle();
22 System.out.println(user1==user2);
23 }
24}
25
首先定义了一个
Recycler的成员变量
RECYCLER, 在匿名内部类中重写了
newObject方法
, 也就是创建对象的方法
, 该方法就是用户自定义的
这里
newObject返回的
new User(handle), 代表当回收站没有此类对象的时候
, 可以通过这种方式创建对象
成员变量
RECYCLER, 可以用来对此类对象的回收和再利用
定一个了一个静态内部类
User, User中有个成员变量
handle, 在构造方法中为其赋值
, handle的作用
, 就是用于对象回收的
并且定义了一个方法
recycle, 方法体中通过
handle.recycle(this)这种方式将自身对象进行回收
, 通过这步操作
, 就可以将对象回收到
Recycler中
以上逻辑先做了解
, 之后会进行详细分析
在
main方法中
, 通过
RECYCLER的
get方法获取一个
user, 然后进行回收
再通过
get方法将回收站的对象取出
, 再次进行回收
, 最后判断两次取出的对象是否为一个对象
, 最后结果输出为
true
以上
demo就可以说明
Recycler的回收再利用的功能
简单介绍了
demo, 我们就详细的分析
Recycler的机制
在
Recycler的类的源码中
, 我们看到这一段逻辑
:
1
2
3
4
5
6
7
8 1private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
2 @Override
3 protected Stack<T> initialValue() {
4 return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
5 ratioMask, maxDelayedQueuesPerThread);
6 }
7};
8
这一段逻辑我们并不陌生
, 在上一小节的学习中我们知道
, 这里用于保存线程共享对象
, 而这里的共享对象
, 就是一个
Stack类型的对象
每个
stack中维护着一个
DefaultHandle类型的数组
, 用于盛放回收的对象
, 有关
stack和线程的关系如图所示:
8-3-1
也就是说在每个
Recycler中
, 都维护着一个线程共享的栈
, 用于对一类对象的回收
跟到
Stack的构造方法中
:
1
2
3
4
5
6
7
8
9
10
11 1Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
2 int ratioMask, int maxDelayedQueues) {
3 this.parent = parent;
4 this.thread = thread;
5 this.maxCapacity = maxCapacity;
6 availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
7 elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];
8 this.ratioMask = ratioMask;
9 this.maxDelayedQueues = maxDelayedQueues;
10}
11
首先介绍几个构造方法中初始化的关键属性:
属性
parent表示
Reclycer对象自身
属性
thread表示当前
stack绑定的哪个线程
属性
maxCapacity表示当前
stack的最大容量
, 表示
stack最多能盛放多少个元素
属性
elements, 就表示
stack中存储的对象
, 类型为
DefaultHandle, 可以被外部对象引用
, 从而实现回收
属性
ratioMask是用来控制对象回收的频率的
, 也就是说每次通过
Reclycer回收对象的时候
, 不是每次都会进行回收
, 而是通过该参数控制回收频率
属性
maxDelayedQueues, 这里稍微有些复杂
, 在很多时候
, 一个线程创建的对象
, 有可能会被另一个线程所释放
, 而另一个线程释放的对象是不会放在当前线程的
stack中的
, 而是会存放在一个叫做
WeakOrderQueue的数据结构中
, 里面也是存放着一个个
DefaultHandle, WeakOrderQueue会存放线程
1创建的并且在线程
2进行释放的对象
这里只是稍作了解
, 之后的会对此做详细剖析
, 这里我们只需知道
, maxDelayedQueues属性的意思就是我这个线程能回收几个其他创建的对象的线程, 假设当前线程是线程1,
maxDelayedQueues为2, 那么我线程1回收了线程2创建的对象, 又回收了线程3创建的对象, 那么不可能回收线程4创建的对象了, 因为
maxDelayedQueues2, 我只能回收两个线程创建的对象
属性
availableSharedCapacity, 表示在线程
1中创建的对象
, 在其他线程中缓存的最大个数
, 同样
, 相关逻辑会在之后的内容进行剖析
另外介绍两个没有在构造方法初始化的属性:
1
2
3 1private WeakOrderQueue cursor, prev;
2private volatile WeakOrderQueue head;
3
这里相当于指针
, 用于指向
WeakOrderQueue的
, 这里也是稍作了解
, 之后会进行详细剖析
有关
stack异线程之间对象的关系如图所示
(简略
):
8-3-2
我们再继续介绍
Recycler的构造方法
, 同时熟悉有关
stack各个参数的默认值:
1
2
3
4 1protected Recycler() {
2 this(DEFAULT_MAX_CAPACITY_PER_THREAD);
3}
4
这里调用了重载的构造方法
, 并传入了参数
DEFAULT_MAX_CAPACITY_PER_THREAD
DEFAULT_MAX_CAPACITY_PER_THREAD
的默认值是
32768, 在
static块中被初始化的
, 我们可以跟进去自行分析
这个值就代表的每个线程中
, stack中最多回收的元素的个数
继续跟重载的构造方法:
1
2
3
4 1protected Recycler(int maxCapacityPerThread) {
2 this(maxCapacityPerThread, MAX_SHARED_CAPACITY_FACTOR);
3}
4
这里又调用了重载的构造方法
, 并且传入刚才传入的
32768和
MAX_SHARED_CAPACITY_FACTOR
MAX_SHARED_CAPACITY_FACTOR
默认值是
2, 同样在
static块中进行了初始化
, 有关该属性的用处稍后讲解
继续跟构造方法
:
1
2
3
4 1protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor) {
2 this(maxCapacityPerThread, maxSharedCapacityFactor, RATIO, MAX_DELAYED_QUEUES_PER_THREAD);
3}
4
这里同样调用了重载的构造方法
, 传入了刚才
32768和
2, 还有两个属性
RATIO和
MAX_DELAYED_QUEUES_PER_THREAD
RATIO
也在
static中被初始化
, 默认值是
8
同上
, MAX_DELAYED_QUEUES_PER_THREAD的默认值是
2倍
cpu核数
我们继续跟构造方法
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
2 int ratio, int maxDelayedQueuesPerThread) {
3 ratioMask = safeFindNextPositivePowerOfTwo(ratio) - 1;
4 if (maxCapacityPerThread <= 0) {
5 this.maxCapacityPerThread = 0;
6 this.maxSharedCapacityFactor = 1;
7 this.maxDelayedQueuesPerThread = 0;
8 } else {
9 this.maxCapacityPerThread = maxCapacityPerThread;
10 this.maxSharedCapacityFactor = max(1, maxSharedCapacityFactor);
11 this.maxDelayedQueuesPerThread = max(0, maxDelayedQueuesPerThread);
12 }
13}
14
这里将几个属性进行了初始化
首先看
ratioMask, 这里的方法
safeFindNextPositivePowerOfTwo的参数
ratio为
8, 该方法的意思就是大于等于
8的
2的幂次方
-1, 这里就是
ratioMask就是
7
maxCapacityPerThread
是刚才分析的
32768, 是一个大于
0的数
, 所以进入
else
maxCapacityPerThread
为
32768
maxSharedCapacityFactor
的值为
2
maxDelayedQueuesPerThread
的值为
2倍
CPU核数
我们再回到
Stack的构造方法中
:
1
2
3
4
5
6
7
8
9
10
11 1Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
2 int ratioMask, int maxDelayedQueues) {
3 this.parent = parent;
4 this.thread = thread;
5 this.maxCapacity = maxCapacity;
6 availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
7 elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];
8 this.ratioMask = ratioMask;
9 this.maxDelayedQueues = maxDelayedQueues;
10}
11
根据
Recycler初始化属性的逻辑
, 我们可以知道
Stack中几个属性的值
:
maxCapacity
默认值为
32768
ratioMask
默认值为
7
maxDelayedQueues
默认值是两倍
cpu核数
availableSharedCapacity
的默认值是
32768/2, 也就是
16384
以上就是
Recycler创建的相关逻辑
上一节: FastThreadLocal的set方法
下一节: recycler中获取对象
posted on
2019-01-02 15:40 向南是个万人迷 阅读(
…) 评论(
…) 编辑 收藏