netty-对象池实现Recycler用法测试

您知道什么是Tomcat对象池技术吗?【从原理到实践完全指南】 本文深入解析Tomcat中的对象池技术,重点剖析SynchronizedStack实现原理。对象池通过复用对象减少GC压力,提升系统吞吐量,适用于创建成本高、使用频繁的对象。文章详细讲解对象池的设计思想、实现细节(包括线程安全、数组扩容策略等)、适用场景及性能调优建议,并对比不同实现方案的优劣。通过完整代码示例和性能测试,展示对象池的实际效果,帮助开发者理解何时以及如何正确使用对象池技术。最后强调工程实践中应根据实际场景权衡利弊,避免过度优化。 阅读详情
2017年写这篇文章的时候,关于Netty对象池Recylcer.java源码解读的文章还很少,但当时由于时间匆忙,仅仅给出了Recycler使用的示例代码,但是没有做深入的源码解读。
今天(2019-02-18)回头过来整理的时候,发现网上一下子涌现出了许多篇介绍Recycler.java的文章,并且,已经有介绍的比较详细的文章,所以本人已经不打算进一步分析源码了。此处暂且列出几篇优秀的博文供大家查阅。
1. Netty之Recycler
2. netty源码分析4 - Recycler对象池的设计
但是网站的这些文章,虽然已经详细分析了Recycler.java。但是却没有一张个人认为拿得出手的Recycler图示。所以,本人仅仅提供一张更加详细的Recycler内部结构图,希望这张图能够帮助有兴趣的朋友们深入的理解这个类。
Recycler结构简图:

Recycler结构图


Recycler用法示例

import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class RecyclerTest01 {

    private static AtomicInteger objId = new AtomicInteger(1);
    private static CountDownLatch latch = new CountDownLatch(1);
    private static PooledObj[] objSlot = new PooledObj[4];

    private static  CountDownLatch async_thread_latch = new CountDownLatch(2);
    private static  AtomicInteger async_thread_id = new AtomicInteger(99);

    private static Recycler<PooledObj> pool = new Recycler<PooledObj>() {
        @Override
        protected PooledObj newObject(Handle<PooledObj> handle) {
            PooledObj obj = new PooledObj("obj-" + objId.getAndIncrement(), handle);

            return obj;
        }
    };


    static class PooledObj {

        private Recycler.Handle<PooledObj> handle = null;
        private String name = null;

        public PooledObj(String name, Recycler.Handle<PooledObj> handle) {
            this.name = name;
            this.handle = handle;
        }

        public Recycler.Handle<PooledObj> getHandle() {
            return handle;
        }

        public void setHandle(Recycler.Handle<PooledObj> handle) {
            this.handle = handle;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }

        public void recycle() {
            handle.recycle(this);
        }
    }

    static class GetRunnable implements Runnable {
        private CountDownLatch latch;
        private boolean nosync;
        private int index;


        public GetRunnable(CountDownLatch latch, boolean nosync, int index) {
            this.latch = latch;
            this.nosync = nosync;
            this.index = index;
        }

        @Override
        public void run() {
            PooledObj obj = pool.get();
            objSlot[index] = obj;

            System.out.println(Thread.currentThread().getName() + " 获取对象: " + obj);

            if (!nosync) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 僵住等待停止");
                    latch.await();

                    PooledObj anotherObj = pool.get();
                    System.out.println(Thread.currentThread().getName() + " 线程被唤醒, 并且得到对象" + anotherObj);

                    String name = Thread.currentThread().getName();

                    Thread asyncRecycleThread = new Thread(() -> {
                        anotherObj.recycle();
                        async_thread_latch.countDown();
                    }, "async-thread-" + async_thread_id.getAndIncrement() + "-versus-" + name );
                    asyncRecycleThread.setDaemon(false);
                    asyncRecycleThread.start();


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    @Test
    public void testRecycler() {

        //主线程从对象池中取对象obj-1,赋值给objSlot[0]
        PooledObj obj0 = pool.get();
        objSlot[0] = obj0;

        System.out.println("主线程从对象池获取对象" + obj0);

        //线程thread-1从对象池中取对象obj-2,赋值给objSlot[1]
        Thread g1 = new Thread(new GetRunnable(latch, false, 1), "thread-1");
        g1.start();

        //线程thread-2从对象池中取对象obj-3,赋值给objSlot[2]
        Thread g2 = new Thread(new GetRunnable(latch, true, 2), "thread-2");
        g2.start();

        //线程thread-3从对象池中取对象obj-4,赋值给objSlot[3]
        Thread g3 = new Thread(new GetRunnable(latch, false, 3), "thread-3");
        g3.start();


        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //主线程回收对象obj-1到主线程的对象池,保存到Stack对象中
        objSlot[0].recycle();

        //主线程回收对象obj-2到thread-1的对象池,保存到WeakOrderQueue节点中
        objSlot[1].recycle();

        //主线程回收对象obj-3到thread-2的对象池,保存到WeakOrderQueue节点中
        objSlot[2].recycle();

        //主线程回收对象obj-4到thread-3的对象池,保存到WeakOrderQueue节点中
        objSlot[3].recycle();

        //主线程取出obj-1
        PooledObj obj = pool.get();
        System.out.println("回收后取到对象: " + obj);

        //主线程取出对象,但由于obj-1已经被取出,所以新建对象obj-5返回
        PooledObj obj2 = pool.get();
        System.out.println("回收后取到对象: " + obj2);

        //主线程取出对象,但由于obj-1已经被取出,所以新建对象obj-6返回
        PooledObj obj3 = pool.get();
        System.out.println("回收后取到对象: " + obj3);

        //主线程取出对象,但由于obj-1已经被取出,所以新建对象obj-7返回
        PooledObj obj4 = pool.get();
        System.out.println("回收后取到对象: " + obj4);

        //主线程回收obj-1到Stack对象中
        obj.recycle();
        //主线程回收obj-5到Stack对象中
        obj2.recycle();
        //主线程回收obj-6到Stack对象中
        obj3.recycle();
        //主线程回收obj-7到Stack对象中
        obj4.recycle();

        latch.countDown();

        try {
            async_thread_latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}


运行上述测试代码,控制台打印如下日志,可见示例代码已经模拟了对象被自身线程回收和被其他线程回收两种情况:
main get STACK: main-stack
主线程从对象池获取对象obj-1
thread-1 get STACK: thread-1-stack
thread-1 获取对象: obj-2
thread-1 僵住等待停止
thread-2 get STACK: thread-2-stack
thread-2 获取对象: obj-3
thread-3 get STACK: thread-3-stack
thread-3 获取对象: obj-4
thread-3 僵住等待停止
回收后取到对象: obj-1
main get STACK: main-stack
回收后取到对象: obj-5
main get STACK: main-stack
回收后取到对象: obj-6
main get STACK: main-stack
回收后取到对象: obj-7
thread-1 线程被唤醒, 并且得到对象obj-2
thread-3 线程被唤醒, 并且得到对象obj-4
异步回收线程async-thread-99-versus-thread-1 开始回收对象
异步回收线程async-thread-100-versus-thread-3 开始回收对象
Recycler源码如下,下述的源码为了配合上面的用法示例,本人已经在必要的地方添加了日志。注: Recycler对象池相关的源码已经抽取为一个框架

/*
 * Copyright 2013 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */



import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import static   io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo;
import static java.lang.Math.max;
import static java.lang.Math.min;

/**
 * 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) {
            // NOOP
        }
    };
    private static final AtomicInteger ID_GENERATOR = new AtomicInteger(Integer.MIN_VALUE);
    private static final int OWN_THREAD_ID = ID_GENERATOR.getAndIncrement();
    private static final int DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD = 4 * 1024; // Use 4k instances as default.
    private static final int DEFAULT_MAX_CAPACITY_PER_THREAD;
    private static final int INITIAL_CAPACITY;
    private static final int MAX_SHARED_CAPACITY_FACTOR;
    private static final int MAX_DELAYED_QUEUES_PER_THREAD;
    private static final int LINK_CAPACITY;
    private static final int RATIO;

    static {
        // In the future, we might have different maxCapacity for different object types.
        // e.g. io.netty.   maxCapacity.writeTask
        //      io.netty.   maxCapacity.outboundBuffer
        int maxCapacityPerThread = SystemPropertyUtil.getInt("io.netty.   maxCapacityPerThread",
                SystemPropertyUtil.getInt("io.netty.   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.   maxSharedCapacityFactor",
                        2));

        MAX_DELAYED_QUEUES_PER_THREAD = max(0,
                SystemPropertyUtil.getInt("io.netty.   maxDelayedQueuesPerThread",
                        // We use the same value as default EventLoop number
                        NettyRuntime.availableProcessors() * 2));

        LINK_CAPACITY = safeFindNextPositivePowerOfTwo(
                max(SystemPropertyUtil.getInt("io.netty.   linkCapacity", 16), 16));

        // By default we allow one push to a Recycler for each 8th try on handles that were never recycled before.
        // This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation
        // bursts.
        RATIO = safeFindNextPositivePowerOfTwo(SystemPropertyUtil.getInt("io.netty.   ratio", 8));

        if (logger.isDebugEnabled()) {
            if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) {
                logger.debug("-Dio.netty.   maxCapacityPerThread: disabled");
                logger.debug("-Dio.netty.   maxSharedCapacityFactor: disabled");
                logger.debug("-Dio.netty.   linkCapacity: disabled");
                logger.debug("-Dio.netty.   ratio: disabled");
            } else {
                logger.debug("-Dio.netty.   maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD);
                logger.debug("-Dio.netty.   maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR);
                logger.debug("-Dio.netty.   linkCapacity: {}", LINK_CAPACITY);
                logger.debug("-Dio.netty.   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>>() {
        @Override
        protected      Stack<T> initialValue() {
            return new      Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
                    ratioMask, maxDelayedQueuesPerThread);
        }

        @Override
        protected void onRemoval(     Stack<T> value) {
            // Let us remove the WeakOrderQueue from the WeakHashMap directly if its safe to remove some overhead
            if (value.threadRef.get() == Thread.currentThread()) {
                if (DELAYED_RECYCLED.isSet()) {
                    DELAYED_RECYCLED.get().remove(value);
                }
            }
        }
    };

    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;
        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) {
            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;
    }

    /**
     * @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> {
        private int lastRecycledId;
        private int recycleId;

        boolean hasBeenRecycled;

        private      Stack<?> stack;
        private Object value;

        DefaultHandle(     Stack<?> stack) {
            System.out.println(Thread.currentThread().getName() + " get STACK: " + stack);
            this.stack = stack;
        }

        @Override
        public void recycle(Object object) {
            if (object != value) {
                throw new IllegalArgumentException("object does not belong to handle");
            }

                 Stack<?> stack = this.stack;
            if (lastRecycledId != recycleId || stack == null) {
                throw new IllegalStateException("recycled already");
            }

            stack.push(this);
        }

        @Override
        public String toString() {
            return "handle-" + value;
        }
    }

    private static final FastThreadLocal<Map<     Stack<?>,      WeakOrderQueue>> DELAYED_RECYCLED =
            new FastThreadLocal<Map<     Stack<?>,      WeakOrderQueue>>() {
                @Override
                protected Map<     Stack<?>,      WeakOrderQueue> initialValue() {
                    return new WeakHashMap<     Stack<?>,      WeakOrderQueue>();
                }
            };

    // 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();

        // Let Link extend AtomicInteger for intrinsics. The Link itself will be used as writerIndex.
        @SuppressWarnings("serial")
        static final class Link extends AtomicInteger {
            private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];

            private int readIndex;
            Link next;
        }

        // This act as a place holder for the head Link but also will reclaim space once finalized.
        // Its important this does not hold any reference to either Stack or WeakOrderQueue.
        static final class Head {
            private final AtomicInteger availableSharedCapacity;

            Link link;

            Head(AtomicInteger availableSharedCapacity) {
                this.availableSharedCapacity = availableSharedCapacity;
            }

            /// TODO: In the future when we move to Java9+ we should use java.lang.ref.Cleaner.
            @Override
            protected void finalize() throws Throwable {
                try {
                    super.finalize();
                } finally {
                    Link head = link;
                    link = null;
                    while (head != null) {
                        reclaimSpace(LINK_CAPACITY);
                        Link next = head.next;
                        // Unlink to help GC and guard against GC nepotism.
                        head.next = null;
                        head = next;
                    }
                }
            }

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

            boolean reserveSpace(int space) {
                return reserveSpace(availableSharedCapacity, space);
            }

            static boolean reserveSpace(AtomicInteger availableSharedCapacity, int space) {
                assert space >= 0;
                for (;;) {
                    int available = availableSharedCapacity.get();
                    if (available < space) {
                        return false;
                    }
                    if (availableSharedCapacity.compareAndSet(available, available - space)) {
                        return true;
                    }
                }
            }
        }

        // chain of data items
        private final Head head;
        private Link tail;
        // pointer to another queue of delayed items for the same stack
        private WeakOrderQueue next;
        private final WeakReference<Thread> owner;
        private final int id = ID_GENERATOR.getAndIncrement();

        private WeakOrderQueue() {
            owner = null;
            head = new Head(null);
        }

        private WeakOrderQueue(Stack<?> stack, Thread thread) {
            tail = new Link();

            // 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.
            head = new Head(stack.availableSharedCapacity);
            head.link = tail;
            owner = new WeakReference<Thread>(thread);
        }

        static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
            final 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);

            if (thread.getName().startsWith("async-thread-")) {
                System.out.println("异步回收线程"+ thread.getName() + " 开始回收对象");
            }

            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 Head.reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)
                    ? newQueue(stack, thread) : null;
        }

        void add(DefaultHandle<?> handle) {
            handle.lastRecycledId = id;

            Link tail = this.tail;
            int writeIndex;
            if ((writeIndex = tail.get()) == LINK_CAPACITY) {
                if (!head.reserveSpace(LINK_CAPACITY)) {
                    // Drop it.
                    return;
                }
                // We allocate a Link so reserve the space
                this.tail = tail = tail.next = new Link();

                writeIndex = tail.get();
            }
            tail.elements[writeIndex] = 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);
        }

        boolean hasFinalData() {
            return tail.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) {
            Link head = this.head.link;
            if (head == null) {
                return false;
            }

            if (head.readIndex == LINK_CAPACITY) {
                if (head.next == null) {
                    return false;
                }
                this.head.link = 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)) {
                        // Drop the object.
                        continue;
                    }
                    element.stack = dst;
                    dstElems[newDstSize ++] = element;
                }

                if (srcEnd == LINK_CAPACITY && head.next != null) {
                    // Add capacity back as the Link is GCed.
                    this.head.reclaimSpace(LINK_CAPACITY);
                    this.head.link = head.next;
                }

                head.readIndex = srcEnd;
                if (dst.size == newDstSize) {
                    return false;
                }
                dst.size = newDstSize;
                return true;
            } else {
                // The destination stack is full already.
                return false;
            }
        }
    }

    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;

        // We store the Thread in a WeakReference as otherwise we may be the only ones that still hold a strong
        // Reference to the Thread itself after it died because DefaultHandle will hold a reference to the Stack.
        //
        // The biggest issue is if we do not use a WeakReference the Thread may not be able to be collected at all if
        // the user will store a reference to the DefaultHandle somewhere and never clear this reference (or not clear
        // it in a timely manner).
        final WeakReference<Thread> threadRef;
        final AtomicInteger availableSharedCapacity;
        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;
        private volatile WeakOrderQueue head;

        private String stackThreadName;

        Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
              int ratioMask, int maxDelayedQueues) {
            this.parent = parent;
            threadRef = new WeakReference<Thread>(thread);
            this.maxCapacity = maxCapacity;
            availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
            elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];
            this.ratioMask = ratioMask;
            this.maxDelayedQueues = maxDelayedQueues;
            this.stackThreadName = thread.getName();
        }

        // Marked as synchronized to ensure this is serialized.
        synchronized void setHead(WeakOrderQueue queue) {
            queue.setNext(head);
            head = queue;
        }

        int increaseCapacity(int expectedCapacity) {
            int newCapacity = elements.length;
            int maxCapacity = this.maxCapacity;
            do {
                newCapacity <<= 1;
            } 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;
            ret.lastRecycledId = 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) {
                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
                    // 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.
                    if (cursor.hasFinalData()) {
                        for (;;) {
                            if (cursor.transfer(this)) {
                                success = true;
                            } else {
                                break;
                            }
                        }
                    }

                    if (prev != null) {
                        prev.setNext(next);
                    }
                } else {
                    prev = cursor;
                }

                cursor = next;

            } while (cursor != null && !success);

            this.prev = prev;
            this.cursor = cursor;
            return success;
        }

        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);
            }
        }

        private void pushNow(DefaultHandle<?> item) {
            if ((item.recycleId | item.lastRecycledId) != 0) {
                throw new IllegalStateException("recycled already");
            }
            item.recycleId = item.lastRecycledId = OWN_THREAD_ID;

            int size = this.size;
            if (size >= maxCapacity || dropHandle(item)) {
                // Hit the maximum capacity or should drop - drop the possibly youngest object.
                return;
            }
            if (size == elements.length) {
                elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
            }

            elements[size] = item;
            this.size = size + 1;
        }

        private void pushLater(DefaultHandle<?> item, Thread thread) {
            // 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
            // we impose a memory ordering here (no-op on x86)
            Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
            WeakOrderQueue queue = delayedRecycled.get(this);
            if (queue == null) {
                if (delayedRecycled.size() >= maxDelayedQueues) {
                    // 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) {
                    // 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) {
                    // Drop the object.
                    return true;
                }
                handle.hasBeenRecycled = true;
            }
            return false;
        }

        DefaultHandle<T> newHandle() {
            return new DefaultHandle<T>(this);
        }

        @Override
        public String toString() {
            return stackThreadName + "-stack";
        }
    }


}


3分钟学会SQL中的断点去重技术,轻松搞定连续重复数据去重问题? 传感器采集的温度数据中,存在连续重复的值,需仅在数值变化时保留记录(例如:温度从 25°C 变为 26°C 时记录一次)。:判断逻辑,current_value!若当前温度与前一行的温度不同(或为第一条记录),则保留该行。的技术,常用于时间序列或状态日志中。:将连续相同值的记录合并,只保留断点处的记录。保留断点处的记录(即温度变化时的第一条)。原始数据表 sensor_data。最终仅保留值变化的断点记录。获取前一行的温度值。 阅读详情

相关推荐

排列显示阿拉伯语、数字及英文时的处理方法

这段时间参与开发沙特阿拉伯的网站模板,模板使用的语言是阿拉伯语,其显示顺序为从右到左,而中文、数字及英文的显示顺序为从左到右 ,所以当遇到一行文字内既有阿拉伯语又有数字和英文时会出现语序混乱的问题,如 期待显示的文案为: 按顺序写入,对应的代码如以下,其中currency_pay为变量,值为356SAR <span>الدفع الفعلي:{{currency_pay}}</span> 显示为 الدفع الفعلي : 356SAR 金额356和货币符号SAR分开了,这样显

llq886的博客 5807

Netty学习十四:内存管理之Recycler 对象池

Netty学习系列

weixin_41402069的博客 1490

关于Vscode配置Unity环境时的一些报错问题(持续更新)

3.找到设置文件,添加如下代码,用于告诉VSCode)在处理 C# 项目时优先使用 C# 扩展来加载和管理项目,而不是使用 C# Dev Kit 或其他扩展。1.很可能是unity的插件版本问题,打开window > package manager将。2.很可能是项目文件的问题,建议unity打开edit > perfercenes。3.手动重装其他版本的.Net框架(去官网找,多下几个无所谓的)解决:(由于造成问题不一样,所以建议都尝试一次)下载net请求超时(一般都会超时很正常的)

bug制造机 1890

Netty源码解析(八)之Recycler

当代码中存在大量的对某一个类的对象进行创建和销毁时,可以使用对象池(类似于线程池),因为对象的创建是耗费一定时间的,因此使用线程池可以在一定程度上提升程序的性能。例如,在PooledByteBufAllocator中,需要不停的对内存进行回收和释放,相应的就会不停地创建和回收ByteBuf,因此Netty中提供了一个对象池工具类——Recycler......

benjam1n77的博客 880

Simplorer-Maxwell联合仿真速度太慢?我用这3个技巧把仿真时间砍半(附SVPWM周期与步长设置避坑)

本文详细介绍了Simplorer-Maxwell联合仿真性能优化的3个关键技巧,包括仿真步长与周期的科学配置、基于任务调度的加速技术以及模型简化与精度平衡。特别针对SVPWM参数设置提供了避坑指南,帮助工程师在不牺牲精度的情况下将仿真时间缩短50%以上,适用于电机驱动系统开发等场景。

weixin_29189381的博客 466

锐捷交换机配置SSH

锐捷交换机配置ssh 一、组网需求 通过ssh功能可以远程登录管理设备 二、配置步骤 全局开启ssh服务,并且指定ssh版本 Ruijie>enable Ruijie#configure terminal Ruijie(config)#enable service ssh-server ------>开启SSH服务,默认关闭 Ruijie(config)#ip ssh version 2 ------>默认1.99版本,设置为版本2 添加登

Loners_fan的博客 1万+

Netty对象池 Recycler

Netty对象池 Recycler 对于为什么要使用对象池,肯定是提高性能啊! 对象的创建,回收,再创建;很耗损jvm性能的,所以就有了对象池的概念,Netty通过Recycler管理对象的创建与回收,而在对象回收时,也不是真正的把对象从堆内存中移除了,而是在内存中缓存了,那具体什么时候回收呢?对象有一个WeakReference,GC回收就与弱引用有关了。具体请查看:强引用、软引用、弱引用、虚引用他们之间的区别了。 一、对象池组件介绍 1、Recycler Recycler是对所有组件的一个管理,通过

Kelvin_hui的博客 2612

数学建模:相关性分析学习——皮尔逊(pearson)相关系数与斯皮尔曼(spearman)相关系数

相关性分析:求解相关系数并绘制热力图进行对比

weixin_67565775的博客 8万+

netty Recycler对象池

之前我们也分析过,这也是一个池化实现,在redis客户端也有应用,但它是通过加锁解决并发问题的,设计没有netty这么精细。上面的源码来自netty4.1.42,从整体上看整个Recycler的设计还是比较复杂的,主要为了解决多线程竞争和GC问题,导致整个代码复杂度比较高,所以netty在后来的版本中对其进行重构。不过这不影响我们对它思想的学习,以后也可以借鉴到实际开发中。

qq_64170174的博客 1038

多机器人协同:水下机器人集群的分布式控制与ROS仿真实践

本文探讨了多自主水下机器人(AUV)集群的分布式控制方法,重点分析了欠驱动6自由度AUV模型的反馈线性化技术。通过ROS/Gazebo仿真平台,详细介绍了系统架构设计、通信延迟补偿策略及环境扰动应对方案,为水下协同作业提供实用参考。

jupi8的博客 688

103.在 Vue 3 中使用 OpenLayers 控制图层来显示/隐藏地图

是一个强大的开源库,可以用于构建各种地理信息系统应用。在 Vue 3 中,我们可以结合。方法,可以直接控制某个图层是否可见,而不需要删除或重新创建图层。在开始之前,确保你已经搭建了 Vue 3 项目,并安装了。本文将详细介绍如何在 Vue 3 组合式 API (,并实现按钮控制地图的显示/隐藏功能。会导致 OSM 瓦片地图无法加载。方法控制地图图层的显示与隐藏。安装完成后,即可开始编写代码。在本教程中,我们介绍了如何在。来实现地图的显示/隐藏功能。来实现交互式地图,并通过。在 WebGIS 开发中,

小彭的博客 1561

Netty 的对象池 Recycler 是什么

该文所涉及的 netty 源码版本为 4.1.6。

黎明就要到来 1002

热力学仿真软件:Fluent_(8).材料属性设置

在 Fluent 中,材料属性主要包括密度、比热容、导热系数、黏度等物理性质。这些属性在不同的仿真场景中有着不同的作用,例如在流体仿真中,密度和黏度是必不可少的;在传热仿真中,导热系数和比热容是主要考虑的因素。如果所需的材料不在材料库中,或者需要对现有材料进行修改,用户可以自定义材料属性。在主菜单中选择Materials。点击打开材料编辑器。在材料编辑器中,点击Create按钮,输入新的材料名称。选择材料类型(如FluidSolid等)。在属性栏中填写所需的材料属性。

kkchenjj的博客 1495

Netty对象池技术详解:原理、实现与最佳实践

/ 重置状态Netty对象池通过精妙的线程局部存储+层级化回收机制,在保证线程安全的前提下实现了高效的对象复用。合理使用该技术可使QPS提升5-10倍,同时降低GC停顿时间达80%以上。建议在开发网络应用时优先考虑使用对象池管理高频创建的对象。

一位努力搬砖程序员。在这里,我会分享关于编程技巧、项目实战经验以及对新技术的见解。希望通过我的文章能够帮助你在技术路上更进一步。期待与你共同学习和成长! 991

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Netty精粹之轻量级内存池技术实现原理与应用

在Netty中,通常会有多个IO线程独立工作,基于NioEventLoop的实现,每个IO线程负责轮询单独的Selector实例来检索IO事件,当IO事件来临的时候,IO线程开始处理IO事件。最常见的IO事件即读写事件,那么这个时候就会涉及到IO线程对数据的读写问题,具体到NIO方面即从内核缓...

336

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Netty从0到1系列之Recycler对象池技术【1】

本文介绍了Netty框架中的Recycler对象池技术,该技术通过复用短生命周期对象来减少GC压力。Recycler具有线程隔离、无锁化和弹性伸缩三大特性,通过对象回收机制提升性能。文中给出了一个User类的示例,展示如何配合Recycler.Handle实现对象回收。该系列完整教程包含37篇技术文章,涵盖Netty核心组件(I/O模型、NIO、Buffer等)、线程模型、内存管理以及拆包粘包处理等关键技术点,适合开发者系统学习Netty框架。

编程技术圈 970

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

基于Netty包中的Recycler实现的对象池技术详解

一、业务背景 当项目中涉及到频繁的对象的创建和回收的时候,就会出现频繁GC的情况,这时就出现了池化的技术来实现对象的循环使用从而避免对象的频繁回收,Netty包下的Recycler就实现了这一功能。当创建对象的时候直接从池中获取,但使用完毕进行回收的时候, 直接将对象回收到池中,这样可以大量减少对象的创建和回收,是对JVM优化的很好的手段 二、Recycler的使用案例 首先定义...

Lee_0202的博客 508

Netty Recycler对象池:原理、优化与实践指南

对象池技术是提升Java应用性能的核心手段之一,通过复用对象减少GC开销。其实现原理主要基于内存预分配与生命周期管理,在高并发场景下可显著降低Young GC频率。Netty Recycler作为轻量级线程本地对象池,采用栈式存储与无锁设计,相比通用对象池性能提升30%以上。典型应用包括网络编程中的ByteBuf复用、消息中间件中的对象缓存等场景。本文以Recycler为例,深入解析其线程本地化存储、双路径回收等机制,并给出maxCapacityPerThread等关键参数的调优建议,帮助开发者解决内存泄漏

weixin_30580341的博客 375

Netty4 Recycler

Recycler Recycler是Netty经常使用的对象池抽象类,通过重用对象,能够避免频繁创建对象和销毁对象带来的损耗。 1. Recycler 抽象类介绍 1. 使用了FastThreadLocal // 每个线程都有自己的stack private final FastThreadLocal<Stack<T>> threadLocal =...

weixin_41651116的博客 567

Netty源码(十五):Recycler工具类

1. Recycler工具类的使用 Recycler为了避免我们重复的创建对象,使用对象池将我们使用过的数据保存起来,下一次就可以拿出来使用 public class TestRecycler { // 对象池 private static final Recycler<User> RECYCLER = new Recycler<User>() { ...

流的博客 410

Netty-对象池原理

# Netty-对象池原理

Java老鸟 370

java内存池实现_Netty精粹之轻量级内存池技术实现原理与应用

在Netty中,通常会有多个IO线程独立工作,基于NioEventLoop的实现,每个IO线程负责轮询单独的Selector实例来检索IO事件,当IO事件来临的时候,IO线程开始处理IO事件。最常见的IO事件即读写事件,那么这个时候就会涉及到IO线程对数据的读写问题,具体到NIO方面即从内核缓冲区读取数据到用户缓冲区或者从用户缓冲区将数据写到内核缓冲区。NIO提供了两种Buffer作为缓冲区,即D...

weixin_30696229的博客 395

深入netty22-netty性能优化和最佳实践

内存池和对象池都是 Netty 中用于优化资源管理的技术。内存池通过预先分配内存块并按需分配,减少了直接缓冲区分配的开销。对象池通过重用对象,减少了对象创建和销毁的开销,同时对 JVM 的垃圾回收也是友好的。这两种技术都有助于构建高性能且稳定的网络应用程序。

m0_63833709的博客 1972

netty源码学习第三章:ByteBuf

先来学习几个概念: 一、对象池技术 对象池其实就是缓存一些对象从而避免大量创建同一个类型的对象,类似线程池的概念。对象池缓存了一些已经创建好的对象,避免需要时才创建对象,同时限制了实例的个数。池化技术最终要的就是重复的使用池内已经创建的对象。从上面的内容就可以看出对象池适用于以下几个场景: 创建对象的开销大 会创建大量的实例 限制一些资源的使用 Apache Commons P

captain0101的博客 301

Netty的内存管理

Netty是一个高性能的异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器与客户端。

kiingking的博客 672
上一篇: netty - DefaultAttributeMap用法测试
下一篇: jdk之BitSet位运算解析
lhever_
博客等级 码龄14年 213粉丝 130原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值