Netty源码 Recycler 对象池全面解析
来源:互联网 发布:js有集合吗 编辑:程序博客网 时间:2024/06/11 09:45
众所周知Netty是一个高性能的NIO通信框架, Netty开发者都是很厉害的, 不仅框架设计很精巧,针对高性能做了很多优化, 很多细节方面都处理的很好. 做中间件用C语言开发当然是速度最快的, 用Java做NIO通信中间件, 需要对java语言有很深的认识, 做到扬长弊短.
比如Java的面向对象编程, 提高了开发的效率, 但是面向对象编程的附属品垃圾回收机制, 在高并发场景下还是一个瓶颈, 特别是Netty的I/O通信采用的是非堆内存, 对这种内存对象垃圾回收效率会比JVM的堆内存效率低, 频繁的GC对虚拟机性能有很大影响. Netty中打交道最多个就是各种Buffer, 通信I/O都是在处理Buffer对象. 因为Buf对象实在使用太频繁了, 哪怕提升一点点, 对性能也是有不少提升的.
下面我们就来看下Netty在这方面做了哪些优化. io.netty.util.Recycler 类是Netty自定义的一个对象池, 借助于这个类可以从对象池中获取对象, 使用完了回收到对象池中, 减少GC.我现在看的版本是Netty的4.1.13.Final版, 差不多是最新的版本了. Netty的开发社区还是很活跃的, 很多代码都在持续优化中, 有bug也都是能及时修复的.比如Recycler这个类, 也是一直在持续优化中, 比老版本的类功能更强大.
1. Recycler
这是一个抽象类, 子类继承它时需要实现 newObject(Handle handle) 方法, 因为不同的子类针对不同的对象进行池化, 具体是什么对象由子类自己实现.
内部是用一个对象数组维护缓存的对象.
1. io.netty.util.Recycler#get方法 : 是获取对象池中对象的入口, 如果有可用对象,直接返回, 没有就调用上面提到的newObject方法创建一个.
2. io.netty.util.Recycler#recycle方法: 是对象使用完毕后回收对象的, 现在已经标记@Deprecated不建议使用了, 建议使用io.netty.util.Recycler.Handle方法. 可以看下4.0.x版本中都是用的这个方法, 这个方法只能回收当前线程创建的对象,负责直接返回false不回收,交给GC处理了. 方法io.netty.util.Recycler.DefaultHandle#recycle优势就是, 可以回收不是当前线程创建的对象, 复用性和性能更好了.
2. Stack
这是Recycler中定义的一个类, 主要也只有Recycler会访问到, Stack就是维护对象池的数据结构了.上面提到的io.netty.util.Recycler#get方法是从FastThreadLocal从获取池中对象,所以这个对象池是各个线程独立的,也就是每个线程都对应一个Stack.
FastThreadLocal是Netty自己定义的一个类似于JDK的ThreadLocal但是性能更高的一个数据结构,ThreadLocal内部访问数据是Map型的数据访问,FastThreadLocal内部是数组,直接通过索引访问会更快.
3. WeakOrderQueue
这个Recycler私有的一个类,用来暂存待回收的对象.
上面提到了io.netty.util.Recycler.DefaultHandle#recycle方法,可以回收不是当前线程创建的对象. 主要就是靠这个类实现的.
recycle方法判断,如果是当前线程创建的对象, 直接就把对象放到当前线程对应的Stack中. 如果不是, 则放入WeakOrderQueue中, 当然WeakOrderQueue会和回收的对象所对应的Stack是相关联的. 在从Stack获取对象池时,如果对象池为空,会尝试从对应的WeakOrderQueue中恢复对象,这样就实现了回收的功能. WeakOrderQueue可以有多个, 多个WeakOrderQueue是一个链表结构, 可以依次访问.
还有个东西要注意下,由于Stack是每个线程的本地变量,所以Stack里的所有方法都是线程安全的. 具体还用到了各种线程安全问题, 还有WeakReference弱引用的使用, 以及位运算控制内存池缓慢增长 等各种细节方面都考虑的很好, 确实很厉害. 要做到对这个类有充分的理解, 需要熟悉java的GC机制, 这块我后面还要再研究一下.
4. Recycler 源码全面解析 (详细说明和注释)
/** * Light-weight object pool based on a thread-local stack. // 一个轻量的对象池 * * @param <T> the type of the pooled object */public abstract class Recycler<T> { private static final InternalLogger logger = InternalLoggerFactory.getInstance(Recycler.class); @SuppressWarnings("rawtypes") private static final Handle NOOP_HANDLE = new Handle() { // 表示一个不需要回收的包装对象 @Override public void recycle(Object object) { // 用于maxCapacityPerThread == 0时,关闭对象回收功能. // NOOP } }; private static final AtomicInteger ID_GENERATOR = new AtomicInteger(Integer.MIN_VALUE); // 线程安全的自增计数器,用来做唯一标记的. private static final int OWN_THREAD_ID = ID_GENERATOR.getAndIncrement(); //static变量, 生成并获取一个唯一id, 标记当前的线程. private static final int DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD = 32768; // Use 32k instances as default. private static final int DEFAULT_MAX_CAPACITY_PER_THREAD; // 每个线程的Stack最多缓存多少个对象 private static final int INITIAL_CAPACITY; // 初始化容量 private static final int MAX_SHARED_CAPACITY_FACTOR; // 最大可共享的容量 private static final int MAX_DELAYED_QUEUES_PER_THREAD; // WeakOrderQueue最大数量 private static final int LINK_CAPACITY; // WeakOrderQueue中的数组DefaultHandle<?>[] elements容量 private static final int RATIO; // 掩码 static { // 这里做到了各种参数都是可配置的, 可以根据实际的压测情况, 调节对象池的参数 // In the future, we might have different maxCapacity for different object types. // e.g. io.netty.recycler.maxCapacity.writeTask // io.netty.recycler.maxCapacity.outboundBuffer int maxCapacityPerThread = SystemPropertyUtil.getInt("io.netty.recycler.maxCapacityPerThread", SystemPropertyUtil.getInt("io.netty.recycler.maxCapacity", DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD)); if (maxCapacityPerThread < 0) { maxCapacityPerThread = DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD; } DEFAULT_MAX_CAPACITY_PER_THREAD = maxCapacityPerThread; MAX_SHARED_CAPACITY_FACTOR = max(2, SystemPropertyUtil.getInt("io.netty.recycler.maxSharedCapacityFactor", 2)); MAX_DELAYED_QUEUES_PER_THREAD = max(0, SystemPropertyUtil.getInt("io.netty.recycler.maxDelayedQueuesPerThread", // We use the same value as default EventLoop number NettyRuntime.availableProcessors() * 2)); LINK_CAPACITY = safeFindNextPositivePowerOfTwo( max(SystemPropertyUtil.getInt("io.netty.recycler.linkCapacity", 16), 16)); // By default we allow one push to a Recycler for each 8th try on handles that were never recycled before. // 默认每过8次,允许回收一次 // This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation // 可以让recycler的容量缓慢的增大,避免爆发式的请求吧. // bursts. RATIO = safeFindNextPositivePowerOfTwo(SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8)); // 值默认为8, 也就是二进制 1000 if (logger.isDebugEnabled()) { if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) { logger.debug("-Dio.netty.recycler.maxCapacityPerThread: disabled"); logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: disabled"); logger.debug("-Dio.netty.recycler.linkCapacity: disabled"); logger.debug("-Dio.netty.recycler.ratio: disabled"); } else { logger.debug("-Dio.netty.recycler.maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD); logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR); logger.debug("-Dio.netty.recycler.linkCapacity: {}", LINK_CAPACITY); logger.debug("-Dio.netty.recycler.ratio: {}", RATIO); } } INITIAL_CAPACITY = min(DEFAULT_MAX_CAPACITY_PER_THREAD, 256); } private final int maxCapacityPerThread; private final int maxSharedCapacityFactor; private final int ratioMask; private final int maxDelayedQueuesPerThread; private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() { //FastThreadLocal是线程本地变量, 所以每个线程都对应一个自己的Stack. @Override protected Stack<T> initialValue() { return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor, ratioMask, maxDelayedQueuesPerThread); } }; protected Recycler() { this(DEFAULT_MAX_CAPACITY_PER_THREAD); } protected Recycler(int maxCapacityPerThread) { this(maxCapacityPerThread, MAX_SHARED_CAPACITY_FACTOR); } protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor) { this(maxCapacityPerThread, maxSharedCapacityFactor, RATIO, MAX_DELAYED_QUEUES_PER_THREAD); } protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor, int ratio, int maxDelayedQueuesPerThread) { ratioMask = safeFindNextPositivePowerOfTwo(ratio) - 1; //根据ratio获取一个掩码,默认为8,那么ratioMask二进制就是 "111" if (maxCapacityPerThread <= 0) { this.maxCapacityPerThread = 0; this.maxSharedCapacityFactor = 1; this.maxDelayedQueuesPerThread = 0; } else { this.maxCapacityPerThread = maxCapacityPerThread; this.maxSharedCapacityFactor = max(1, maxSharedCapacityFactor); this.maxDelayedQueuesPerThread = max(0, maxDelayedQueuesPerThread); } } @SuppressWarnings("unchecked") public final T get() { if (maxCapacityPerThread == 0) { // 通过修改maxCapacityPerThread=0可以关闭回收功能, 默认值是32768 return newObject((Handle<T>) NOOP_HANDLE); } Stack<T> stack = threadLocal.get(); // 获取当前线程对应的Stack DefaultHandle<T> handle = stack.pop(); // 从对象池获取对象 if (handle == null) { handle = stack.newHandle(); handle.value = newObject(handle); // 没有对象,则调用子类的newObject方法创建新的对象 } return (T) handle.value; } /** * @deprecated use {@link Handle#recycle(Object)}. //旧的方法 */ @Deprecated public final boolean recycle(T o, Handle<T> handle) { if (handle == NOOP_HANDLE) { return false; } DefaultHandle<T> h = (DefaultHandle<T>) handle; if (h.stack.parent != this) { // 旧的方法,如果不是当前线程的, 直接不回收了. return false; } h.recycle(o); return true; } final int threadLocalCapacity() { return threadLocal.get().elements.length; } final int threadLocalSize() { return threadLocal.get().size; } protected abstract T newObject(Handle<T> handle); public interface Handle<T> { void recycle(T object); } static final class DefaultHandle<T> implements Handle<T> { // DefaultHandle就是就是Stack的包装对象,持有stack的引用,可以回收自己到stack中; private int lastRecycledId; //标记最新一次回收的线程id private int recycleId; //也是一个标记,是用来回收前的校验的. boolean hasBeenRecycled; //标记是否已经被回收 private Stack<?> 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中 } } private static final FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED = // 这也是一个线程本地变量,每个线程都有自己的Map<Stack<?>, WeakOrderQueue> new FastThreadLocal<Map<Stack<?>, WeakOrderQueue>>() { // 根据Stack可以获取到对应的WeakOrderQueue @Override // 需要注意的是这边两个对象都有弱引用,WeakReference! 具体下面解释. protected Map<Stack<?>, WeakOrderQueue> initialValue() { return new WeakHashMap<Stack<?>, WeakOrderQueue>(); // 使用WeakHashMap,保证对key也就是Stack是弱引用; 一旦Stack没有强引用了, 会被回收的,WeakHashMap不会无限占用内存; } }; // a queue that makes only moderate guarantees about visibility: items are seen in the correct order, // but we aren't absolutely guaranteed to ever see anything at all, thereby keeping the queue cheap to maintain private static final class WeakOrderQueue { static final WeakOrderQueue DUMMY = new WeakOrderQueue(); //用于标记空的WeakOrderQueue,在达到WeakOrderQueue数量上限时放入一个这个,表示结束了. // Let Link extend AtomicInteger for intrinsics. The Link itself will be used as writerIndex. // Link对象本身会作为读索引. @SuppressWarnings("serial") private static final class Link extends AtomicInteger { //这里为什么要继承一个AtomicInteger呢,因为这样Link就是一个线程安全的容器,保证了多线程安全和可见性. private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY]; //维护一个数组, 容量默认为16. private int readIndex; //读索引 private Link next; //下一个索引. WeakOrderQueue有多个时, 之间遍历靠next指向下一个WeakOrderQueue. } // chain of data items private Link head, tail; //头指针和尾指针 // pointer to another queue of delayed items for the same stack private WeakOrderQueue next; //指向下一个WeakOrderQueue private final WeakReference<Thread> owner; // 拥有者,干嘛的?? 要注意到这是一个弱引用,就是不会影响Thread对象的GC的,如果thread为空,owner.get()会返回null private final int id = ID_GENERATOR.getAndIncrement(); // WeakOrderQueue的唯一标记 private final AtomicInteger availableSharedCapacity; // 允许的最大共享容量 private WeakOrderQueue() { //用于初始化DUMMY,遇到DUMMY就知道要抛弃了. owner = null; availableSharedCapacity = null; } private WeakOrderQueue(Stack<?> stack, Thread thread) { //在Stack的io.netty.util.Recycler.Stack.pushLater()中如果没有WeakOrderQueue,会调用这里new一个 head = tail = new Link(); //初始化头和尾指针,指向这个新创建的Link owner = new WeakReference<Thread>(thread); //表示当前的WeakOrderQueue是被哪个线程拥有的. 因为只有不同线程去回收对象才会进到这个方法,所以thread不是这stack对应的线程 //这里的WeakReference,对Thread是一个弱引用,所以Thread在没有强引用时就会被回收(线程也是可以回收的对象) // Its important that we not store the Stack itself in the WeakOrderQueue as the Stack also is used in //这里很重要,我们没有把Stack保存到WeakOrderQueue中 // the WeakHashMap as key. So just store the enclosed AtomicInteger which should allow to have the //因为Stack是WeakHashMap的key // Stack itself GCed. //我们只是持有head 和 tail的引用,就可以遍历WeakOrderQueue availableSharedCapacity = stack.availableSharedCapacity; } static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) { //stack.setHead(queue)必须在构造器外进行,防止对象溢出.(我查看作者的该动记录是为了修改以前的不安全发布的构造方法) 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); //这个stack,头指针指向 这个新创建的WeakOrderQueue return queue; } private void setNext(WeakOrderQueue next) { assert next != this; this.next = next; } /** * Allocate a new {@link WeakOrderQueue} or return {@code null} if not possible. */ static WeakOrderQueue allocate(Stack<?> stack, Thread thread) { // We allocated a Link so reserve the space return reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY) //先预约space容量 ? WeakOrderQueue.newQueue(stack, thread) : null; //预约成功, 对当前stack创建一个新的WeakOrderQueue } private static boolean reserveSpace(AtomicInteger availableSharedCapacity, int space) { //容量不够就返回false; 够的话就减去space大小. assert space >= 0; for (;;) { int available = availableSharedCapacity.get(); if (available < space) { //如果剩余可用容量小于 LINK_CAPACITY,返回false return false; } if (availableSharedCapacity.compareAndSet(available, available - space)) { //调用availableSharedCapacity线程安全的CAS方法 return true; } } } private void reclaimSpace(int space) { //availableSharedCapacity加上space,就是恢复前面减去的space大小 assert space >= 0; availableSharedCapacity.addAndGet(space); //availableSharedCapacity和上面的方法会存在并发,所以采用原子类型. } void add(DefaultHandle<?> handle) { handle.lastRecycledId = id; //更新最近一次回收的id, 注意这里只更新了lastRecycledId, recycleId没有更新, 等到真正回收的时候,会改成一致的. Link tail = this.tail; int writeIndex; if ((writeIndex = tail.get()) == LINK_CAPACITY) { if (!reserveSpace(availableSharedCapacity, LINK_CAPACITY)) { //判断剩余空间是否足够 // Drop it. return; } // We allocate a Link so reserve the space this.tail = tail = tail.next = new Link(); writeIndex = tail.get(); //tail这是一个自增的变量,每次tail.get()就表示放到末尾了 } tail.elements[writeIndex] = handle; //把对应的handle引用放到末尾的数组里 handle.stack = null; // we lazy set to ensure that setting stack to null appears before we unnull it in the owning thread; // this also means we guarantee visibility of an element in the queue if we see the index updated tail.lazySet(writeIndex + 1); //todo 这个方法JDK注释比较少,还没看懂.后面可以写个demo测试下. } boolean hasFinalData() { return tail.readIndex != tail.get(); //readIndex指向当前读取的, tail.get()表示最大的值, 不相等代表还有待读取的数据. } // transfer as many items as we can from this queue to the stack, returning true if any were transferred @SuppressWarnings("rawtypes") boolean transfer(Stack<?> dst) { //把WeakOrderQueue里面暂存的对象,传输到对应的stack,主动去回收对象. 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; //前面的add方法只更新了lastRecycledId, transfer执行好了,需要更新recycleId一致,表示回收成功. } else if (element.recycleId != element.lastRecycledId) { //recycleId=0才表示可回收的 throw new IllegalStateException("recycled already"); } srcElems[i] = null; //成功了,就把WeakOrderQueue数组里置为空,释放对对象的引用 if (dst.dropHandle(element)) { //判断是否回收 // Drop the object. continue; } element.stack = dst; //element是Link数组里的对象,stack指向目标stack dstElems[newDstSize ++] = element; //目标Stack数组的尾部, 放入element } if (srcEnd == LINK_CAPACITY && head.next != null) { //如果head.next还有,就需要继续扩容 // Add capacity back as the Link is GCed. reclaimSpace(LINK_CAPACITY); //扩容 this.head = head.next; //指向下一个,等待下一次循环继续上面的操作.transfer方法外层是被循环调用的. } head.readIndex = srcEnd; //下次从这里开始读 if (dst.size == newDstSize) { //如果相等则表示没有剩余空间了,返回false return false; } dst.size = newDstSize; //目标数组size修改 return true; } else { // The destination stack is full already. // 目标仍然是满的,直接返回false,就不做回收动作 return false; } } @Override protected void finalize() throws Throwable { //WeakOrderQueue对象GC前调用这个方法 try { super.finalize(); //回收对象 } finally { // We need to reclaim all space that was reserved by this WeakOrderQueue so we not run out of space in // the stack. This is needed as we not have a good life-time control over the queue as it is used in a //需要这个方法是因为这个被用在WeakHashMap中,会随时GC它 // WeakHashMap which will drop it at any time. Link link = head; while (link != null) { //遍历WeakHashMap中所有的Link,直到link = null这样里面的Link对象没有引用了,都会被回收. reclaimSpace(LINK_CAPACITY); link = link.next; } } } } static final class Stack<T> { // we keep a queue of per-thread queues, which is appended to once only, each time a new thread other // than the stack owner recycles: when we run out of items in our stack we iterate this collection // to scavenge those that can be reused. this permits us to incur minimal thread synchronisation whilst //使用最少的同步操作,并且可以全部回收. // still recycling all items. final Recycler<T> parent; final Thread thread; //持有线程的强引用, 如果Stack没有被回收,那么Thread也不能被回收了,但是Stack没有强引用,在map中是弱引用,前面提到的.关于引用的知识回头再细化下. final AtomicInteger availableSharedCapacity; //容量,用一个AtomicInteger表示,是为了可以并发CAS修改; final int maxDelayedQueues; private final int maxCapacity; private final int ratioMask; private DefaultHandle<?>[] elements; private int size; private int handleRecycleCount = -1; // Start with -1 so the first one will be recycled. private WeakOrderQueue cursor, prev; // 指向当前的WeakOrderQueue 和 前一个 private volatile WeakOrderQueue head; Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor, int ratioMask, int maxDelayedQueues) { this.parent = parent; this.thread = thread; this.maxCapacity = maxCapacity; availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY)); //maxSharedCapacityFactor默认为2 elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)]; //取初始值和maxCapacity较小的一个; 如果INITIAL_CAPACITY < maxCapacity后面可以动态扩容 this.ratioMask = ratioMask; this.maxDelayedQueues = maxDelayedQueues; } // Marked as synchronized to ensure this is serialized. //标记为同步,保证两个操作顺序执行 synchronized void setHead(WeakOrderQueue queue) { // 重要的一点就是这个方法是 synchronized 的, 这个类里面唯一的synchronized方法 queue.setNext(head); // synchronized避免并发修改queue.setNext的情况. head = queue; } int increaseCapacity(int expectedCapacity) { int newCapacity = elements.length; int maxCapacity = this.maxCapacity; do { newCapacity <<= 1; //每次扩容两倍,直到newCapacity大于expectedCapacity } while (newCapacity < expectedCapacity && newCapacity < maxCapacity); newCapacity = min(newCapacity, maxCapacity); if (newCapacity != elements.length) { elements = Arrays.copyOf(elements, newCapacity); } return newCapacity; } @SuppressWarnings({ "unchecked", "rawtypes" }) 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; //获取出的对象,置为0表示没有被回收 ret.lastRecycledId = 0; //获取出的对象,置为0表示没有被回收 this.size = size; return ret; } boolean scavenge() { // continue an existing scavenge, if any if (scavengeSome()) { return true; } // reset our scavenge cursor prev = null; cursor = head; return false; } boolean scavengeSome() { //尝试回收 WeakOrderQueue prev; WeakOrderQueue cursor = this.cursor; //指向当前的指针 if (cursor == null) { //当前为null,就指向head,head也为null就跳出返回false prev = null; cursor = head; if (cursor == null) { return false; } } else { prev = this.prev; } boolean success = false; do { if (cursor.transfer(this)) { success = true; break; } WeakOrderQueue next = cursor.next; if (cursor.owner.get() == null) { //线程被回收了 // If the thread associated with the queue is gone, unlink it, after //cursor.owner.get() == null表示,WeakOrderQueue的归属线程被回收了. // performing a volatile read to confirm there is no data left to collect. //这里读取的是线程安全的变量,确认没有数据可回收了 // We never unlink the first queue, as we don't want to synchronize on updating the head. //第一个queue永远不回收,因为更新head指针会存在并发. if (cursor.hasFinalData()) { for (;;) { if (cursor.transfer(this)) { success = true; } else { break; //cursor.transfer(this)返回false,代表没有读取的数据了 } } } if (prev != null) { prev.setNext(next); //这是一个单向链表,只要改变prev的引用,老的节点会被回收的. } } else { prev = cursor; } cursor = next; } while (cursor != null && !success); this.prev = prev; this.cursor = cursor; return success; } void push(DefaultHandle<?> item) { //会综合判断,如果是当前线程,直接放进数组中,如果不是,就先报错到WeakOrderQueue中. Thread currentThread = Thread.currentThread(); if (thread == 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, we need to signal that the push //保存到WeakOrderQueue,等待回收. // happens later. pushLater(item, currentThread); } } private void pushNow(DefaultHandle<?> item) { // 立即push,把item对象回收到elements数组中 if ((item.recycleId | item.lastRecycledId) != 0) { // 如果没回收,recycleId和lastRecycledId应该都是0; 正常应该不会进来, 感觉应该是作者为了在开发中排除这种情况. throw new IllegalStateException("recycled already"); } item.recycleId = item.lastRecycledId = OWN_THREAD_ID; //都更新为OWN_THREAD_ID,表示被回收过了 int size = this.size; if (size >= maxCapacity || dropHandle(item)) { //如果size >= maxCapacity, 就会执行dropHandle() // Hit the maximum capacity or should drop - drop the possibly youngest object. return; //dropHandle()返回true,就直接return掉,本次不做回收. } if (size == elements.length) { //如果size == elements.length,就要对elements数组进行扩容,每次扩容2倍,最大到maxCapacity elements = Arrays.copyOf(elements, min(size << 1, maxCapacity)); } elements[size] = item; //这里要注意: elements.length是数组的长度,包括空位; size是数组中有内容的长度, 这里是在最末尾放item; this.size = size + 1; //size+1 } private void pushLater(DefaultHandle<?> item, Thread thread) { // 想想为什么需要pushLater? // we don't want to have a ref to the queue as the value in our weak map // so we null it out; to ensure there are no races with restoring it later //为了在回收的过程中没有并发,如果回收的不是当前线程的Stack的对象, // we impose a memory ordering here (no-op on x86) //就放入到它的WeakOrderQueue,等它自己拿的时候回收,这样recycle方法就没有并发了;这种思想在Doug lea的AQS里也有. Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get(); //获取当前线程对应的Map<Stack<?>, WeakOrderQueue> WeakOrderQueue queue = delayedRecycled.get(this); //根据this Stack获取 WeakOrderQueue if (queue == null) { //如果queue就需要创建一个 if (delayedRecycled.size() >= maxDelayedQueues) { //大于上限,就放入一个DUMMY,表示满了 // Add a dummy queue so we know we should drop the object delayedRecycled.put(this, WeakOrderQueue.DUMMY); return; } // Check if we already reached the maximum number of delayed queues and if we can allocate at all. if ((queue = WeakOrderQueue.allocate(this, thread)) == null) { //WeakOrderQueue.allocate方法,针对需要回收的这个Stack,创建一个新的WeakOrderQueue // drop object return; } delayedRecycled.put(this, queue); } else if (queue == WeakOrderQueue.DUMMY) { // drop object return; } queue.add(item); } boolean dropHandle(DefaultHandle<?> handle) { if (!handle.hasBeenRecycled) { //判断是否已经回收 if ((++handleRecycleCount & ratioMask) != 0) { //handleRecycleCount初始为-1, ++handleRecycleCount = 0, 所以第一次肯定会进去.位运算的性能很好. // Drop the object. //ratioMask是一个掩码,解释见下方 return true; } handle.hasBeenRecycled = true; } return false; } DefaultHandle<T> newHandle() { return new DefaultHandle<T>(this); } }}
注:
dropHandle方法里有用到一个掩码ratioMask, 这个必须是2的次方-1,
如果按默认值7 (“111”) , 只要低三位不全0, 就会返回true本次忽略不做回收. 所以返回true概率是 7/8, 目的是为了让回收动作缓慢一些, 内存池慢慢的增加, 减少对系统的压力. 不得不说作者考虑的真仔细.
110111001011101111000 <--100110101011110011011110111110000 <--
5. 下面写个Demo,看下效果
package comm;import com.github.ltsopensource.core.support.SystemClock;import io.netty.util.Recycler;import org.junit.Test;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.concurrent.CountDownLatch;public class RecyclerTest { static class WrapRecycler { private List<String> list; private final static Recycler<WrapRecycler> RECYCLER = new Recycler<WrapRecycler>() { @Override protected WrapRecycler newObject(Handle<WrapRecycler> handle) { return new WrapRecycler(handle); } }; Recycler.Handle<WrapRecycler> handle; WrapRecycler(Recycler.Handle<WrapRecycler> handle) { this.handle = handle; this.list = new ArrayList<>(1000); } List<String> getList() { return list; } static WrapRecycler getInstance() { return RECYCLER.get(); } void recycle() { handle.recycle(this); } } @Test public void testDifferentThreadRecycle() throws InterruptedException, IOException { System.out.println("Main thread started ..."); final WrapRecycler instance = WrapRecycler.getInstance(); instance.getList().add("111"); // main 线程放入一个字符串 final CountDownLatch countDownLatch = new CountDownLatch(1); new Thread(new Runnable() { // 这里新创建一个线程,在新的线程中可以回收main线程中的对象. @Override public void run() { System.out.println("Sub thread started ..."); List<String> list = instance.getList(); list.add("222"); // 子线程放入一个字符串. instance.recycle(); // 对main线程从对象池中回去的对象家进行回收动作. System.out.println("Sub Thread get list : " + WrapRecycler.getInstance().getList()); // 在子线程中从对象池获取对象 countDownLatch.countDown(); } }).start(); countDownLatch.await(); System.out.println("Main Thread get list : " + WrapRecycler.getInstance().getList()); // 在主线程中从对象池获取对象 System.in.read(); }}
执行结果:
可以看到执行结果, 子线程可以回收主线程从主线程对象池中的对象, 但是只是回收到主线程对应的对象池里了, 各个线程对应的对象池时独立的.
Main thread started ...Sub thread started ...Sub Thread get list : []Main Thread get list : [111, 222]
进而可以写两个demo,比较下使用对象池和依赖JVM垃圾回收的执行效率.
我本地测试下来, 发现同样都是在堆内存下, 如果创建的都是很小的对象, 使用netty的这个对象池没有什么优势, 但是如果创建的是比较复杂比较大的对象, 使用对象池执行时间只有前者的七分之一. 理论上, 对象垃圾回收的成本越高, 这个对象池的效果就会越明显的, 特别是在Netty的非堆内存场景下, 垃圾回收的成本会更高.
大家如果在代码里要使用的话, 可以针对自己的场景进行测试下.
6. 思考
- 在学习一些优秀的开源软件代码时, 我们不一定就要自己再写一套中间件, 但是其中的某些优点和思路在平时的开发中是值得借鉴的, 比如在做相关的网络编程时, 如果对netty的实现有一定的了解, 就能把一些好的设计思路用起来.
- 这个类的主要作者就是netty的一位核心开发人员: normanmaurer, 现在就职于苹果. 在研究这个类时, 由于注释很少, 代码晦涩难懂. 我在学习这个类时, 翻看了netty的各个版本, 从github的提交记录可以看到这个类最早创建于2013-5-28日, 其实在netty3.x的版本中是没有这个类的, 从4.x开始的版本中增加了这个类, 用于提升netty的性能. 从2013年到2017年, 这个类从无到有, 从简单到复杂, 一直在持续优化中. 在研究代码的过程中,我通过对比github上的历史代码, 看起来就相对好理解一些了, 一些复杂的实现其实不是刚开始就这么复杂的, 都是每次一点点的增强.
- 前面学习JDK的源码,也发现了这样规律, 比如很多concurrent包中的类从1.5就有了,但是后续的jdk版本中都在持续优化, 代码会变的比较复杂, 我在先看了JDK1.5之后, 再看1.6和1.7,就能明白一些地方作者的用意了.
- 一个功能点想出来就是一个好的开始, 实现出来然后需要在后续的实际应用中不断的优化和增强, 代码开发永远不是一蹴而就的, 不一定非要一次就把所有的点都想全, 好的实现可以从一个简单的想法开始.
- Netty源码 Recycler 对象池全面解析
- Netty源码分析(七)—Recycler对象池分析
- netty-对象池实现Recycler用法测试
- Netty之轻量级对象池Recycler
- Netty源码解析
- 【Netty源码解析】NioEventLoop
- netty源码解析
- Netty bytebuf 源码解析
- 解读io.netty.util.Recycler
- 源码全面解析---LruCache
- netty程序源码句子解析
- 全面解析Server对象
- 全面解析Server对象
- 全面解析Server对象
- 全面解析Server对象
- 全面解析Server对象
- 全面解析Server对象
- 全面解析Server对象
- 51nod 1391 01串
- 文化传统的变迁·《这世界,原该天清地宁》
- 机器学习_集成学习
- Team them up! UVA
- MySQL中的数据类型和Java中的对应
- Netty源码 Recycler 对象池全面解析
- hadoop执行mapreduce过程reduce不执行原因
- Samba文件共享
- 探究webassembly(1):初认识
- 文本文件与二进制文件的读写
- Faster-RCNN训练修改
- Java 六大时间类
- Linux系统查看系统是32位还是64位方法总结
- Java语言基础组成(下)