Recyclers对象池设计

前言

不管是netty还是sofa-jraft,使用对象池的目的就是避免重复创建对象,消耗内存,减少GC压力。

 

原理

https://www.jianshu.com/p/854b855bd198

 

Recyclers在Jraft中的使用测试案例

主要测试了在多线程模型下,能够获取和回收同一个对象。

以及在max<0 的时候,每次都会创建一个新的不同对象。

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.
 */
package com.alipay.sofa.jraft.util;

import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;

import org.junit.Assert;
import org.junit.Test;

import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

/**
 *
 * @author jiachun.fjc
 */
public class RecyclersTest {

    private static Recyclers<RecyclableObject> newRecyclers(final int max) {
        return new Recyclers<RecyclableObject>(max) {

            @Override
            protected RecyclableObject newObject(final Recyclers.Handle handle) {
                return new RecyclableObject(handle);
            }
        };
    }

    @Test(expected = IllegalStateException.class)
    public void testMultipleRecycle() {
        final Recyclers<RecyclableObject> recyclers = newRecyclers(16);
        final RecyclableObject object = recyclers.get();
        recyclers.recycle(object, object.handle);
        recyclers.recycle(object, object.handle);
    }

    @Test
    public void testMultipleRecycleAtDifferentThread() throws InterruptedException {
        final Recyclers<RecyclableObject> recyclers = newRecyclers(512);
        final RecyclableObject object = recyclers.get();
        final Thread thread1 = new Thread(() -> recyclers.recycle(object, object.handle));
        thread1.start();
        thread1.join();
        assertSame(object, recyclers.get());
    }

    @Test(expected = IllegalStateException.class)
    public void testRecycleMoreThanOnceAtDifferentThread() throws InterruptedException {
        final Recyclers<RecyclableObject> recyclers = newRecyclers(1024);
        final RecyclableObject object = recyclers.get();

        final AtomicReference<IllegalStateException> exceptionStore = new AtomicReference<>();
        final Thread thread1 = new Thread(() -> recyclers.recycle(object, object.handle));
        thread1.start();
        thread1.join();

        final Thread thread2 = new Thread(() -> {
            try {
                recyclers.recycle(object, object.handle);
            } catch (IllegalStateException e) {
                exceptionStore.set(e);
            }
        });
        thread2.start();
        thread2.join();
        IllegalStateException exception = exceptionStore.get();
        if (exception != null) {
            throw exception;
        }
    }

    @Test
    public void testRecycle() {
        final Recyclers<RecyclableObject> recyclers = newRecyclers(16);
        final RecyclableObject object = recyclers.get();
        recyclers.recycle(object, object.handle);
        final RecyclableObject object2 = recyclers.get();
        Assert.assertSame(object, object2);
        recyclers.recycle(object2, object2.handle);
    }

    @Test
    public void testRecycleDisable() {
        final Recyclers<RecyclableObject> recyclers = newRecyclers(-1);
        final RecyclableObject object = recyclers.get();
        recyclers.recycle(object, object.handle);
        final RecyclableObject object2 = recyclers.get();
        assertNotSame(object, object2);
        recyclers.recycle(object2, object2.handle);
    }

    @Test
    public void testMaxCapacity() {
        testMaxCapacity(300);
        Random rand = new Random();
        for (int i = 0; i < 50; i++) {
            testMaxCapacity(rand.nextInt(1000) + 256); // 256 - 1256
        }
    }

    private static void testMaxCapacity(final int maxCapacity) {
        final Recyclers<RecyclableObject> recyclers = newRecyclers(maxCapacity);
        final RecyclableObject[] objects = new RecyclableObject[maxCapacity * 3];
        for (int i = 0; i < objects.length; i++) {
            objects[i] = recyclers.get();
        }

        for (int i = 0; i < objects.length; i++) {
            recyclers.recycle(objects[i], objects[i].handle);
            objects[i] = null;
        }

        assertTrue("The threadLocalCapacity (" + recyclers.threadLocalCapacity() + ") must be <= maxCapacity ("
                   + maxCapacity + ") as we not pool all new handles internally",
            maxCapacity >= recyclers.threadLocalCapacity());
    }

    static final class RecyclableObject {

        private final Recyclers.Handle handle;

        private RecyclableObject(Recyclers.Handle handle) {
            this.handle = handle;
        }

        public Recyclers.Handle getHandle() {
            return handle;
        }
    }
}

 

SegmentList

*                [segment, segment, segment ...]
* / | \
* segment segment segment
* [0, 1 ... 127] [128, 129 ... 255] [256, 257 ... 383]

创建一个重复利用的对象segment,内部包含一个数据,避免多次分配内存空间。

 

public class SegmentList<T> {
    private static final int             SEGMENT_SHIFT = 7;
    public static final int              SEGMENT_SIZE  = 2 << (SEGMENT_SHIFT - 1);

    private final ArrayDeque<Segment<T>> segments;

    private int                          size;

    // Cached offset in first segment.
    private int                          firstOffset;

    private final boolean                recycleSegment;
    /**
     * A recyclable segment.
     * @author boyan(boyan@antfin.com)
     *
     * @param <T>
     */
    private final static class Segment<T> implements Recyclable {
        private static final  Recyclers<Segment<?>> recyclers = new Recyclers<Segment<?>>(16_382 / SEGMENT_SIZE) {

                                                                 @Override
                                                                 protected Segment<?> newObject(final Handle handle) {
                                                                     return new Segment<>(handle);
                                                                 }
                                                             };

        public static Segment<?> newInstance(final boolean recycleSegment) {
            if (recycleSegment) {
                return recyclers.get();
            } else {
                return new Segment<>();
            }
        }

        private transient Recyclers.Handle handle;

        final T[]                          elements;
        int                                pos;     // end offset(exclusive)
        int                                offset;  // start offset(inclusive)

        Segment() {
            this(Recyclers.NOOP_HANDLE);
        }

        @SuppressWarnings("unchecked")
        Segment(final Recyclers.Handle handle) {
            this.elements = (T[]) new Object[SEGMENT_SIZE];
            this.pos = this.offset = 0;
            this.handle = handle;
        }

        void clear() {
            this.pos = this.offset = 0;
            Arrays.fill(this.elements, null);
        }

        @Override
        public boolean recycle() {
            clear();
            return recyclers.recycle(this, this.handle);
        }

        int cap() {
            return SEGMENT_SIZE - this.pos;
        }

        @SuppressWarnings("SuspiciousSystemArraycopy")
        private void addAll(final Object[] src, final int srcPos, final int len) {
            System.arraycopy(src, srcPos, this.elements, this.pos, len);
            this.pos += len;
        }

        boolean isReachEnd() {
            return this.pos == SEGMENT_SIZE;
        }

        boolean isEmpty() {
            return this.size() == 0;
        }

        void add(final T e) {
            this.elements[this.pos++] = e;
        }

        T get(final int index) {
            if (index >= this.pos || index < this.offset) {
                throw new IndexOutOfBoundsException("Index=" + index + ", Offset=" + this.offset + ", Pos=" + this.pos);
            }
            return this.elements[index];
        }

        T peekLast() {
            return this.elements[this.pos - 1];
        }

        int size() {
            return this.pos - this.offset;
        }

        T peekFirst() {
            return this.elements[this.offset];
        }

        int removeFromLastWhen(final Predicate<T> predicate) {
            int removed = 0;
            for (int i = this.pos - 1; i >= this.offset; i--) {
                T e = this.elements[i];
                if (predicate.test(e)) {
                    this.elements[i] = null;
                    removed++;
                } else {
                    break;
                }
            }
            this.pos -= removed;
            return removed;
        }

        int removeFromFirstWhen(final Predicate<T> predicate) {
            int removed = 0;
            for (int i = this.offset; i < this.pos; i++) {
                T e = this.elements[i];
                if (predicate.test(e)) {
                    this.elements[i] = null;
                    removed++;
                } else {
                    break;
                }
            }
            this.offset += removed;
            return removed;
        }

        int removeFromFirst(final int toIndex) {
            int removed = 0;
            for (int i = this.offset; i < Math.min(toIndex, this.pos); i++) {
                this.elements[i] = null;
                removed++;
            }
            this.offset += removed;
            return removed;
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            for (int i = this.offset; i < this.pos; i++) {
                b.append(this.elements[i]);
                if (i != this.pos - 1) {
                    b.append(", ");
                }
            }
            return "Segment [elements=" + b.toString() + ", offset=" + this.offset + ", pos=" + this.pos + "]";
        }

    }

 

posted @ 2021-09-05 23:36  gaojy  阅读(90)  评论(0编辑  收藏  举报