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 + "]"; } }