软件编程
位置:首页>> 软件编程>> java编程>> Netty分布式获取异线程释放对象源码剖析

Netty分布式获取异线程释放对象源码剖析

作者:向南是个万人迷  发布时间:2021-12-28 09:45:42 

标签:Netty,分布式,异线程,释放对象

前文传送门:异线程下回收对象

获取异线程释放对象

上一小节分析了异线程回收对象, 原理是通过与stack关联的WeakOrderQueue进行回收

如果对象经过异线程回收之后, 当前线程需要取出对象进行二次利用, 如果当前stack中为空, 则会通过当前stack关联的WeakOrderQueue进行取出, 这也是这一小写要分析的, 获取异线程释放的对象

在介绍之前我们首先看Stack类中的两个属性

private WeakOrderQueue cursor, prev;
private volatile WeakOrderQueue head;

这里都是指向WeakOrderQueue的指针, 其中head我们上一小节分析过, 指向最近创建的和stack关联WeakOrderQueue, 也就是头结点

cursor代表的是寻找的当前WeakOrderQueue, pre则是cursor上一个节点, 如图所示:

Netty分布式获取异线程释放对象源码剖析

8-7-1

我们从获取对象的入口方法, handle的get开始分析

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对象通过pop弹出一个handle

我们跟到pop方法中

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;
   ret.lastRecycledId = 0;
   this.size = size;
   return ret;
}

这里我们重点关注, 如果size为空, 也就是当前tack为空的情况下, 会走到scavenge方法, 这个方法, 就是从WeakOrderQueue获取对象的方法

跟进scavenge方法

boolean scavenge() {
   if (scavengeSome()) {
       return true;
   }
   prev = null;
   cursor = head;
   return false;
}

scavengeSome方法表示已经回收到了对象, 则直接返回, 如果没有回收到对象, 则将prev和cursor两个指针进行重置

继续跟到scavengeSome方法中

boolean scavengeSome() {
   WeakOrderQueue cursor = this.cursor;
   if (cursor == null) {
       cursor = head;
       if (cursor == null) {
           return false;
       }
   }
   boolean success = false;
   WeakOrderQueue prev = this.prev;
   do {
       if (cursor.transfer(this)) {
           success = true;
           break;
       }
       WeakOrderQueue next = cursor.next;
       if (cursor.owner.get() == null) {
           if (cursor.hasFinalData()) {
               for (;;) {
                   if (cursor.transfer(this)) {
                       success = true;
                   } else {
                       break;
                   }
               }
           }
           if (prev != null) {
               prev.next = next;
           }
       } else {
           prev = cursor;
       }
       cursor = next;
   } while (cursor != null && !success);
   this.prev = prev;
   this.cursor = cursor;
   return success;
}

首先拿到cursor指针, cursor指针代表要回收的WeakOrderQueue

如果cursor为空, 则让其指向头节点, 如果头节点也空, 说明当前stack没有与其关联的WeakOrderQueue, 则返回false

通过一个布尔值success标记回收状态

然后拿到pre指针, 也就是cursor的上一个节点, 之后进入一个do-while循环

do-while循环的终止条件是, 如果没有遍历到最后一个节点并且回收的状态为false, 这里我们可以分析到再循环体里, 是不管遍历与stack关联的WeakOrderQueue, 直到弹出对象为止

跟到do-while循环中:

首先cursor指针会调用transfer方法, 该方法表示从当前指针指向的WeakOrderQueue中将元素放入到当前stack中, 如果取出成功则将success设置为true并跳出循环, transfer我们稍后分析, 我们继续往下看

如果没有获得元素, 则会通过next属性拿到下一个WeakOrderQueue, 然后会进入一个判断 if (cursor.owner.get() == null) 

owner属性我们上一小节提到过, 就是与当前WeakOrderQueue关联的一个线程, get方法就是获得关联的线程对象, 如果这个对象为null说明该线程不存在, 则进入if块, 也就是一些清理的工作

if块中又进入一个判断 if (cursor.hasFinalData()) , 这里表示当前的WeakOrderQueue中是否还有数据, 如果有数据则通过for循环将数据通过transfer方法传输到当前stack中, 传输成功的, 将success标记为true

transfer方法是将WeakOrderQueue中一个link中的handle往stack进行传输, 有关link的相关内容, 我们上一小节也进行过分析

所以这里通过for循环将每个link的中的数据传输到stack中

继续往下看, 如果pre节点不为空, 则通过 prev.next = next 将cursor节点进行释放, 也就是pre的下一个节点指向cursor的下一个节点

继续往下看else块中的 prev = cursor 

这里表示如果当前线程还在, 则将prev赋值为cursor, 代表prev后移一个节点

最后通过cursor = next将cursor后移一位, 然后再继续进行循环

循环结束之后, 将stack的prev和cursor属性进行保存

我们跟到transfer方法中, 分析如何将WeakOrderQueue中的handle传输到stack中:

boolean transfer(Stack<?> dst) {
   Link head = this.head;
   if (head == null) {
       return false;
   }
   if (head.readIndex == LINK_CAPACITY) {
       if (head.next == null) {
           return false;
       }
       this.head = head = head.next;
   }
   final int srcStart = head.readIndex;
   int srcEnd = head.get();
   final int srcSize = srcEnd - srcStart;
   if (srcSize == 0) {
       return false;
   }
   final int dstSize = dst.size;
   final int expectedCapacity = dstSize + srcSize;
   if (expectedCapacity > dst.elements.length) {
       final int actualCapacity = dst.increaseCapacity(expectedCapacity);
       srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
   }
   if (srcStart != srcEnd) {
       final DefaultHandle[] srcElems = head.elements;
       final DefaultHandle[] dstElems = dst.elements;
       int newDstSize = dstSize;
       for (int i = srcStart; i < srcEnd; i++) {
           DefaultHandle element = srcElems[i];
           if (element.recycleId == 0) {
               element.recycleId = element.lastRecycledId;
           } else if (element.recycleId != element.lastRecycledId) {
               throw new IllegalStateException("recycled already");
           }
           srcElems[i] = null;
           if (dst.dropHandle(element)) {
               continue;
           }
           element.stack = dst;
           dstElems[newDstSize ++] = element;
       }
       if (srcEnd == LINK_CAPACITY && head.next != null) {
           reclaimSpace(LINK_CAPACITY);
           this.head = head.next;
       }
       head.readIndex = srcEnd;
       if (dst.size == newDstSize) {
           return false;
       }
       dst.size = newDstSize;
       return true;
   } else {
       return false;
   }
}

Netty分布式获取异线程释放对象源码剖析

8-7-2

我们上一小节分析过, WeakOrderQueue是由多个link组成, 每个link通过链表的方式进行关联, 其中head属性指向第一个link, tail属性指向最后一个link

在每个link中有多个handle

在link中维护了一个读指针readIndex, 标记着读取link中handle的位置

我们继续分析transfer方法

首先获取头结点, 并判断头结点是否为空, 如果头结点为空, 说明当前WeakOrderQueue并没有link, 返回false

 if (head.readIndex == LINK_CAPACITY) 这里判断读指针是否为16, 因为link中元素最大数量就是16, 如果读指针为16, 说明当前link中的数据都被取走了

接着判断 head.next == null , 表示是否还有下一个link, 如果没有下一个link, 则说明当前WeakOrderQueue没有元素了, 则返回false

Netty分布式获取异线程释放对象源码剖析

8-7-3

继续往下看, 拿到head节点的读指针和head中元素的数量, 接着计算可以传输元素的大小, 如果大小为0, 则返回false

Netty分布式获取异线程释放对象源码剖析

8-7-4

接着, 拿到当前stack的大小, 当前stack大小加上可以传输的大小表示stack中所需要的容量

 if (expectedCapacity > dst.elements.length) 表示如果需要的容量大于当前stack中所维护的数组的大小, 则将stack中维护的数组进行扩容, 进入if块中

扩容之后会返回actualCapacity, 表示扩容之后的大小

再看 srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd) 这步

srcEnd表示可以从Link中取的最后一个元素的下标

 srcStart + actualCapacity - dstSize 这里我们进行一个拆分, actualCapacity - dstSize表示扩容后大大小-原stack的大小, 也就是最多能往stack中传输多少元素

读指针+可以往stack传输的数量, 可以表示往stack中传输的最后一个下标, 这里的下标和srcEnd中取一个较小的值, 也就是既不能超过stack的容量, 也不能造成当前link中下标越界

继续往下看

 int newDstSize = dstSize 表示初始化stack的下标, 表示stack中从这个下标开始添加数据

然后判断 srcStart != srcEnd , 表示能不能同link中获取内容, 如果不能, 则返回false, 如果可以, 则进入if块中

接着拿到当前link的数组elements和stack中的数组elements

然后通过for循环, 通过数组下标的方式不断的将当前link中的数据放入到stack中

for循环中首先拿到link的第i个元素

接着我们我们关注一个细节

if (element.recycleId == 0) {
   element.recycleId = element.lastRecycledId;
} else if (element.recycleId != element.lastRecycledId) {
   throw new IllegalStateException("recycled already");
}

这里 element.recycleId == 0 表示对象没有被回收过, 如果没有被回收过, 则赋值为lastRecycledId, 我们前面分析过lastRecycledId是WeakOrderQueue中的唯一下标, 通过赋值标记element被回收过

然后继续判断 element.recycleId != element.lastRecycledId , 这表示该对象被回收过, 但是回收的recycleId却不是最后一次回收lastRecycledId, 这是一种异常情况, 表示一个对象在不同的地方被回收过两次, 这种情况则抛出异常

接着将link的第i个元素设置为null

继续往下看:

if (dst.dropHandle(element)) {
   continue;
}

这里表示控制回收站回收的频率, 之前的小节我们分析过, 这里不再赘述

 element.stack = dst 表示将handle的stack属性设置到当前stack

 dstElems[newDstSize ++] = element 这里通过数组的下标的方式将link中的handle赋值到stack的数组中

继续往下看:

if (srcEnd == LINK_CAPACITY && head.next != null) {
   reclaimSpace(LINK_CAPACITY);
   this.head = head.next;
}

这里的if表循环结束后, 如果link中的数据已经回收完毕, 并且还有下一个节点则会进到reclaimSpace方法

我们跟到reclaimSpace方法

private void reclaimSpace(int space) {
   assert space >= 0;
   availableSharedCapacity.addAndGet(space);
}

这里将availableSharedCapacity加上16, 表示WeakOrderQueue还可以继续插入link

继续看transfer方法

 this.head = head.next 表示将head节点后移一个元素

 head.readIndex = srcEnd 表示将读指针指向srcEnd, 下一次读取可以从srcEnd开始

 if (dst.size == newDstSize) 表示没有向stack传输任何对象, 则返回false

否则就通过 dst.size = newDstSize 更新stack的大小为newDstSize, 并返回true

章节小结

        这一章主要讲解了两个性能优化工具了FastThreadLocal和Recycler

        FastThreadLocal和jdk的ThreadLocal功能类似, 只是性能更快, 通过FastTreadLocalThread中的threadLocalMap对象, 通过数组下标的方式进行保存和获取对象

        Recycler是一个轻量级的对象回收站, 用于对象重用, 避免了对象的频繁创建和减轻gc的压力

        Recycler同线程回收对象是通过一个线程共享的stack实现的, 将对象包装成handle并存入stack中

        Reclyer异线程回收对象是将handle存入一个与stack关联的WeakOrderQueue中, 同一个stack中关联的不同WeakOrderQueue由不同的线程创建

        从Recycler获取对象时stack中有值, 则可以直接从stack中获取

        如果stack中没有值则通过stack关联的WeakOrderQueue中获取

来源:https://www.cnblogs.com/xiangnan6122/p/10209540.html

0
投稿

猜你喜欢

  • 我们写的主类中的main()方法是如何被Java虚拟机调用到的?在Java类中的一些方法会被由C/C++编写的HotSpot虚拟机的C/C+
  • Spring如何使用 * 缓存解决循环依赖在没开始文章之前首先来了解一下什么是循环依赖@Componentpublic class A {@A
  • 前言近期有个业务需求,涉及用户付费相关的计算,需要一个日历组件,组件功能如下:仅支持从明天开始选择预定日期仅支持可选范围内的日期日期的选择是
  • 异常的练习:老师用电脑上课。开始思考上课中出现的问题。比如问题是电脑蓝屏。电脑冒烟。要对问题进行描述,封装成对象。可是当冒烟发生后,出现讲课
  • AOP是Spring中的面向切面的编程,这里简单感受一下如何在xml文件中配置一个切面。如上图所示,配置一个切面的主要思路有以下几个步骤。1
  • 逆时针画圆弧,原理:将360度分割成36份,分别标出每10度角度时的坐标点,然后将每个点连接起来。 #include <io
  • 背景介绍1,最近有一个大数据量插入的操作入库的业务场景,需要先做一些其他修改操作,然后在执行插入操作,由于插入数据可能会很多,用到多线程去拆
  • 1. 前言Spring除了IOC和DI,还有另一个杀手锏功能&mdash;&mdash;Spring AOP。AOP是一种面
  • 看前欣赏美图!上才艺!目录 一.顺序结构二.分支结构1.if语句2.switch 语句 三. 循环结构1 .while 循环2. break
  • 刚开始做开发学习的小伙伴可能在有一个知识上面有错误的认知,我们天天写程序是在Idea下写的,运行也是在Idea下运行的。但是实际开发完成后,
  • 本文实例讲述了java中Object类用法。分享给大家供大家参考。具体如下:1、Object类是所有java类的基类如果在类的声明中未使用e
  • 本文实例为大家分享了java排序算法之冒泡排序的具体代码,供大家参考,具体内容如下冒泡排序冒泡排序无疑是最为出名的排序算法之一,从序列的一端
  • 线程(Thread)是并发编程的基础,也是程序执行的最小单元,它依托进程而存在。一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组
  • 步骤一:1.查看Eclipse版本的eclipse--help--About Eclipse,在Eclipseplatfrom那行的Vers
  • spring-MVC实现简单的登录功能,供大家参考,具体内容如下今天我学习了spring-MVC实现简单的登录功能,本篇博客就讲解如何使用s
  • 1、思维导图2、什么是MVC?MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(con
  • 本文实例讲述了Android编程之交互对话框。分享给大家供大家参考,具体如下:1. 在Android SDK中,虽然有许多的窗口,有些类似M
  • 这篇文章主要介绍了java split()使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
  • 有哪些“纪律”是Java程序员所要遵守的?1. 为代码添加注释(Add comments to your code). – 每个人都知道这一
  • 编辑Word文档时,我们有时会突然想增加一段新内容;而将word文档给他人浏览时,有些信息我们是不想让他人看到的。那么如何运用C#编程的方式
手机版 软件编程 asp之家 www.aspxhome.com