2021-05-12 10:49:09 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2021 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:
|
|
|
|
*
|
|
|
|
* https://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.
|
|
|
|
*/
|
2021-05-11 11:24:06 +02:00
|
|
|
package io.netty.buffer.api.pool;
|
|
|
|
|
2021-05-12 10:44:33 +02:00
|
|
|
import io.netty.buffer.api.AllocatorControl;
|
2021-05-11 11:24:06 +02:00
|
|
|
import io.netty.buffer.api.Buffer;
|
|
|
|
import io.netty.buffer.api.MemoryManager;
|
|
|
|
import io.netty.util.internal.StringUtil;
|
|
|
|
|
2021-05-21 15:37:59 +02:00
|
|
|
import java.lang.invoke.MethodHandles;
|
|
|
|
import java.lang.invoke.VarHandle;
|
2021-05-11 11:24:06 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import java.util.concurrent.atomic.LongAdder;
|
|
|
|
|
|
|
|
import static io.netty.buffer.api.pool.PoolChunk.isSubpage;
|
|
|
|
import static java.lang.Math.max;
|
|
|
|
|
2021-05-12 10:44:33 +02:00
|
|
|
class PoolArena extends SizeClasses implements PoolArenaMetric, AllocatorControl {
|
2021-05-21 15:37:59 +02:00
|
|
|
private static final VarHandle SUBPAGE_ARRAY = MethodHandles.arrayElementVarHandle(PoolSubpage[].class);
|
2021-05-11 11:24:06 +02:00
|
|
|
enum SizeClass {
|
|
|
|
Small,
|
|
|
|
Normal
|
|
|
|
}
|
|
|
|
|
2021-05-12 10:46:15 +02:00
|
|
|
final PooledBufferAllocator parent;
|
2021-05-11 11:24:06 +02:00
|
|
|
final MemoryManager manager;
|
|
|
|
|
|
|
|
final int numSmallSubpagePools;
|
|
|
|
final int directMemoryCacheAlignment;
|
|
|
|
private final PoolSubpage[] smallSubpagePools;
|
|
|
|
|
|
|
|
private final PoolChunkList q050;
|
|
|
|
private final PoolChunkList q025;
|
|
|
|
private final PoolChunkList q000;
|
|
|
|
private final PoolChunkList qInit;
|
|
|
|
private final PoolChunkList q075;
|
|
|
|
private final PoolChunkList q100;
|
|
|
|
|
|
|
|
private final List<PoolChunkListMetric> chunkListMetrics;
|
|
|
|
|
|
|
|
// Metrics for allocations and deallocations
|
|
|
|
private long allocationsNormal;
|
|
|
|
|
|
|
|
// We need to use the LongAdder here as this is not guarded via synchronized block.
|
|
|
|
private final LongAdder allocationsSmall = new LongAdder();
|
|
|
|
private final LongAdder allocationsHuge = new LongAdder();
|
|
|
|
private final LongAdder activeBytesHuge = new LongAdder();
|
|
|
|
|
|
|
|
private long deallocationsSmall;
|
|
|
|
private long deallocationsNormal;
|
|
|
|
|
|
|
|
// We need to use the LongAdder here as this is not guarded via synchronized block.
|
|
|
|
private final LongAdder deallocationsHuge = new LongAdder();
|
|
|
|
|
|
|
|
// Number of thread caches backed by this arena.
|
|
|
|
final AtomicInteger numThreadCaches = new AtomicInteger();
|
|
|
|
|
2021-05-12 10:46:15 +02:00
|
|
|
protected PoolArena(PooledBufferAllocator parent, MemoryManager manager, int pageSize,
|
2021-05-11 11:24:06 +02:00
|
|
|
int pageShifts, int chunkSize, int cacheAlignment) {
|
|
|
|
super(pageSize, pageShifts, chunkSize, cacheAlignment);
|
|
|
|
this.parent = parent;
|
|
|
|
this.manager = manager;
|
|
|
|
directMemoryCacheAlignment = cacheAlignment;
|
|
|
|
|
|
|
|
numSmallSubpagePools = nSubpages;
|
|
|
|
smallSubpagePools = newSubpagePoolArray(numSmallSubpagePools);
|
|
|
|
|
|
|
|
q100 = new PoolChunkList(this, null, 100, Integer.MAX_VALUE, chunkSize);
|
|
|
|
q075 = new PoolChunkList(this, q100, 75, 100, chunkSize);
|
|
|
|
q050 = new PoolChunkList(this, q075, 50, 100, chunkSize);
|
|
|
|
q025 = new PoolChunkList(this, q050, 25, 75, chunkSize);
|
|
|
|
q000 = new PoolChunkList(this, q025, 1, 50, chunkSize);
|
|
|
|
qInit = new PoolChunkList(this, q000, Integer.MIN_VALUE, 25, chunkSize);
|
|
|
|
|
|
|
|
q100.prevList(q075);
|
|
|
|
q075.prevList(q050);
|
|
|
|
q050.prevList(q025);
|
|
|
|
q025.prevList(q000);
|
|
|
|
q000.prevList(null);
|
|
|
|
qInit.prevList(qInit);
|
|
|
|
|
|
|
|
chunkListMetrics = List.of(qInit, q000, q025, q050, q075, q100);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static PoolSubpage newSubpagePoolHead() {
|
|
|
|
PoolSubpage head = new PoolSubpage();
|
|
|
|
head.prev = head;
|
|
|
|
head.next = head;
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static PoolSubpage[] newSubpagePoolArray(int size) {
|
|
|
|
return new PoolSubpage[size];
|
|
|
|
}
|
|
|
|
|
2021-05-17 15:15:19 +02:00
|
|
|
UntetheredMemory allocate(PooledAllocatorControl control, PoolThreadCache cache, int size) {
|
2021-05-11 11:24:06 +02:00
|
|
|
final int sizeIdx = size2SizeIdx(size);
|
|
|
|
|
|
|
|
if (sizeIdx <= smallMaxSizeIdx) {
|
2021-05-12 10:44:33 +02:00
|
|
|
return tcacheAllocateSmall(control, cache, size, sizeIdx);
|
2021-05-11 11:24:06 +02:00
|
|
|
} else if (sizeIdx < nSizes) {
|
2021-05-12 10:44:33 +02:00
|
|
|
return tcacheAllocateNormal(control, cache, size, sizeIdx);
|
2021-05-11 11:24:06 +02:00
|
|
|
} else {
|
|
|
|
int normCapacity = directMemoryCacheAlignment > 0
|
|
|
|
? normalizeSize(size) : size;
|
|
|
|
// Huge allocations are never served via the cache so just call allocateHuge
|
|
|
|
return allocateHuge(normCapacity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-17 15:15:19 +02:00
|
|
|
private UntetheredMemory tcacheAllocateSmall(PooledAllocatorControl control, PoolThreadCache cache, final int size,
|
|
|
|
final int sizeIdx) {
|
|
|
|
UntetheredMemory memory = cache.allocateSmall(control, size, sizeIdx);
|
|
|
|
if (memory != null) {
|
2021-05-11 11:24:06 +02:00
|
|
|
// was able to allocate out of the cache so move on
|
2021-05-17 15:15:19 +02:00
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
|
|
|
|
* {@link PoolChunk#free(long)} may modify the doubly linked list as well.
|
|
|
|
*/
|
2021-05-21 15:37:59 +02:00
|
|
|
PoolSubpage head = findSubpagePoolHead(sizeIdx);
|
2021-05-11 11:24:06 +02:00
|
|
|
final boolean needsNormalAllocation;
|
|
|
|
synchronized (head) {
|
|
|
|
final PoolSubpage s = head.next;
|
|
|
|
needsNormalAllocation = s == head;
|
|
|
|
if (!needsNormalAllocation) {
|
|
|
|
assert s.doNotDestroy && s.elemSize == sizeIdx2size(sizeIdx);
|
|
|
|
long handle = s.allocate();
|
|
|
|
assert handle >= 0;
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = s.chunk.allocateBufferWithSubpage(handle, size, cache, control);
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needsNormalAllocation) {
|
|
|
|
synchronized (this) {
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = allocateNormal(size, sizeIdx, cache, control);
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
incSmallAllocation();
|
2021-05-17 15:15:19 +02:00
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
2021-05-17 16:22:33 +02:00
|
|
|
private UntetheredMemory tcacheAllocateNormal(
|
|
|
|
PooledAllocatorControl control, PoolThreadCache cache, int size, int sizeIdx) {
|
2021-05-17 15:15:19 +02:00
|
|
|
UntetheredMemory memory = cache.allocateNormal(this, control, size, sizeIdx);
|
|
|
|
if (memory != null) {
|
2021-05-11 11:24:06 +02:00
|
|
|
// was able to allocate out of the cache so move on
|
2021-05-17 15:15:19 +02:00
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
synchronized (this) {
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = allocateNormal(size, sizeIdx, cache, control);
|
2021-05-11 11:24:06 +02:00
|
|
|
allocationsNormal++;
|
|
|
|
}
|
2021-05-17 15:15:19 +02:00
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method must be called inside synchronized(this) { ... } block
|
2021-05-17 16:22:33 +02:00
|
|
|
private UntetheredMemory allocateNormal(
|
|
|
|
int size, int sizeIdx, PoolThreadCache threadCache, PooledAllocatorControl control) {
|
2021-05-17 15:15:19 +02:00
|
|
|
UntetheredMemory memory = q050.allocate(size, sizeIdx, threadCache, control);
|
|
|
|
if (memory != null) {
|
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = q025.allocate(size, sizeIdx, threadCache, control);
|
|
|
|
if (memory != null) {
|
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = q000.allocate(size, sizeIdx, threadCache, control);
|
|
|
|
if (memory != null) {
|
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = qInit.allocate(size, sizeIdx, threadCache, control);
|
|
|
|
if (memory != null) {
|
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = q075.allocate(size, sizeIdx, threadCache, control);
|
|
|
|
if (memory != null) {
|
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add a new chunk.
|
|
|
|
PoolChunk c = newChunk(pageSize, nPSizes, pageShifts, chunkSize);
|
2021-05-17 15:15:19 +02:00
|
|
|
memory = c.allocate(size, sizeIdx, threadCache, control);
|
|
|
|
assert memory != null;
|
2021-05-11 11:24:06 +02:00
|
|
|
qInit.add(c);
|
2021-05-17 15:15:19 +02:00
|
|
|
return memory;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void incSmallAllocation() {
|
|
|
|
allocationsSmall.increment();
|
|
|
|
}
|
|
|
|
|
2021-05-17 15:15:19 +02:00
|
|
|
private UntetheredMemory allocateHuge(int size) {
|
2021-05-11 11:24:06 +02:00
|
|
|
activeBytesHuge.add(size);
|
|
|
|
allocationsHuge.increment();
|
2021-05-17 16:22:33 +02:00
|
|
|
return new UnpooledUnthetheredMemory(parent, manager, size);
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void free(PoolChunk chunk, long handle, int normCapacity, PoolThreadCache cache) {
|
2021-05-12 16:05:09 +02:00
|
|
|
SizeClass sizeClass = sizeClass(handle);
|
|
|
|
if (cache != null && cache.add(this, chunk, handle, normCapacity, sizeClass)) {
|
|
|
|
// cached so not free it.
|
|
|
|
return;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
2021-05-12 16:05:09 +02:00
|
|
|
freeChunk(chunk, handle, normCapacity, sizeClass);
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static SizeClass sizeClass(long handle) {
|
|
|
|
return isSubpage(handle) ? SizeClass.Small : SizeClass.Normal;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeChunk(PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) {
|
|
|
|
final boolean destroyChunk;
|
|
|
|
synchronized (this) {
|
2021-05-17 16:22:33 +02:00
|
|
|
if (sizeClass == SizeClass.Normal) {
|
|
|
|
++deallocationsNormal;
|
|
|
|
} else if (sizeClass == SizeClass.Small) {
|
|
|
|
++deallocationsSmall;
|
|
|
|
} else {
|
|
|
|
throw new AssertionError("Unexpected size class: " + sizeClass);
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
destroyChunk = !chunk.parent.free(chunk, handle, normCapacity);
|
|
|
|
}
|
|
|
|
if (destroyChunk) {
|
|
|
|
// destroyChunk not need to be called while holding the synchronized lock.
|
|
|
|
chunk.destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PoolSubpage findSubpagePoolHead(int sizeIdx) {
|
2021-05-21 15:37:59 +02:00
|
|
|
PoolSubpage head = (PoolSubpage) SUBPAGE_ARRAY.getVolatile(smallSubpagePools, sizeIdx);
|
|
|
|
if (head == null) {
|
|
|
|
head = newSubpagePoolHead();
|
|
|
|
if (!SUBPAGE_ARRAY.compareAndSet(smallSubpagePools, sizeIdx, null, head)) {
|
|
|
|
// We lost the race. Read the winning value.
|
|
|
|
head = (PoolSubpage) SUBPAGE_ARRAY.getVolatile(smallSubpagePools, sizeIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return head;
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
2021-05-12 10:44:33 +02:00
|
|
|
@Override
|
2021-05-17 15:15:19 +02:00
|
|
|
public UntetheredMemory allocateUntethered(Buffer originator, int size) {
|
2021-05-12 10:44:33 +02:00
|
|
|
throw new AssertionError("PoolChunk base buffers should never need to reallocate.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void recoverMemory(Object memory) {
|
|
|
|
// This means we've lost all strong references to a PoolChunk.
|
|
|
|
// Probably means we don't need it anymore, so just free its memory.
|
|
|
|
manager.discardRecoverableMemory(memory);
|
|
|
|
}
|
|
|
|
|
2021-05-11 11:24:06 +02:00
|
|
|
@Override
|
|
|
|
public int numThreadCaches() {
|
|
|
|
return numThreadCaches.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int numSmallSubpages() {
|
|
|
|
return smallSubpagePools.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int numChunkLists() {
|
|
|
|
return chunkListMetrics.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<PoolSubpageMetric> smallSubpages() {
|
|
|
|
return subPageMetricList(smallSubpagePools);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<PoolChunkListMetric> chunkLists() {
|
|
|
|
return chunkListMetrics;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static List<PoolSubpageMetric> subPageMetricList(PoolSubpage[] pages) {
|
|
|
|
List<PoolSubpageMetric> metrics = new ArrayList<>();
|
2021-05-21 15:37:59 +02:00
|
|
|
for (int i = 0, len = pages.length; i < len; i++) {
|
|
|
|
PoolSubpage head = (PoolSubpage) SUBPAGE_ARRAY.getVolatile(pages, i);
|
|
|
|
if (head == null || head.next == head) {
|
2021-05-11 11:24:06 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
PoolSubpage s = head.next;
|
|
|
|
do {
|
|
|
|
metrics.add(s);
|
|
|
|
s = s.next;
|
|
|
|
} while (s != head);
|
|
|
|
}
|
|
|
|
return metrics;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numAllocations() {
|
|
|
|
final long allocsNormal;
|
|
|
|
synchronized (this) {
|
|
|
|
allocsNormal = allocationsNormal;
|
|
|
|
}
|
|
|
|
|
|
|
|
return allocationsSmall.longValue() + allocsNormal + allocationsHuge.longValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numSmallAllocations() {
|
|
|
|
return allocationsSmall.longValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized long numNormalAllocations() {
|
|
|
|
return allocationsNormal;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numDeallocations() {
|
|
|
|
final long deallocs;
|
|
|
|
synchronized (this) {
|
|
|
|
deallocs = deallocationsSmall + deallocationsNormal;
|
|
|
|
}
|
|
|
|
return deallocs + deallocationsHuge.longValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized long numSmallDeallocations() {
|
|
|
|
return deallocationsSmall;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized long numNormalDeallocations() {
|
|
|
|
return deallocationsNormal;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numHugeAllocations() {
|
|
|
|
return allocationsHuge.longValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numHugeDeallocations() {
|
|
|
|
return deallocationsHuge.longValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numActiveAllocations() {
|
|
|
|
long val = allocationsSmall.longValue() + allocationsHuge.longValue()
|
|
|
|
- deallocationsHuge.longValue();
|
|
|
|
synchronized (this) {
|
|
|
|
val += allocationsNormal - (deallocationsSmall + deallocationsNormal);
|
|
|
|
}
|
|
|
|
return max(val, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numActiveSmallAllocations() {
|
|
|
|
return max(numSmallAllocations() - numSmallDeallocations(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numActiveNormalAllocations() {
|
|
|
|
final long val;
|
|
|
|
synchronized (this) {
|
|
|
|
val = allocationsNormal - deallocationsNormal;
|
|
|
|
}
|
|
|
|
return max(val, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numActiveHugeAllocations() {
|
|
|
|
return max(numHugeAllocations() - numHugeDeallocations(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long numActiveBytes() {
|
|
|
|
long val = activeBytesHuge.longValue();
|
|
|
|
synchronized (this) {
|
|
|
|
for (int i = 0; i < chunkListMetrics.size(); i++) {
|
|
|
|
for (PoolChunkMetric m: chunkListMetrics.get(i)) {
|
|
|
|
val += m.chunkSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max(0, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected final PoolChunk newChunk(int pageSize, int maxPageIdx, int pageShifts, int chunkSize) {
|
2021-05-18 18:20:32 +02:00
|
|
|
return new PoolChunk(this, pageSize, pageShifts, chunkSize, maxPageIdx);
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized String toString() {
|
|
|
|
StringBuilder buf = new StringBuilder()
|
|
|
|
.append("Chunk(s) at 0~25%:")
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append(qInit)
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append("Chunk(s) at 0~50%:")
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append(q000)
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append("Chunk(s) at 25~75%:")
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append(q025)
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append("Chunk(s) at 50~100%:")
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append(q050)
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append("Chunk(s) at 75~100%:")
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append(q075)
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append("Chunk(s) at 100%:")
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append(q100)
|
|
|
|
.append(StringUtil.NEWLINE)
|
|
|
|
.append("small subpages:");
|
|
|
|
appendPoolSubPages(buf, smallSubpagePools);
|
|
|
|
buf.append(StringUtil.NEWLINE);
|
|
|
|
|
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void appendPoolSubPages(StringBuilder buf, PoolSubpage[] subpages) {
|
|
|
|
for (int i = 0; i < subpages.length; i ++) {
|
2021-05-21 15:37:59 +02:00
|
|
|
PoolSubpage head = (PoolSubpage) SUBPAGE_ARRAY.getVolatile(subpages, i);
|
|
|
|
if (head == null || head.next == head) {
|
2021-05-11 11:24:06 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.append(StringUtil.NEWLINE)
|
|
|
|
.append(i)
|
|
|
|
.append(": ");
|
|
|
|
PoolSubpage s = head.next;
|
|
|
|
do {
|
|
|
|
buf.append(s);
|
|
|
|
s = s.next;
|
|
|
|
} while (s != head);
|
|
|
|
}
|
|
|
|
}
|
2021-05-12 10:44:33 +02:00
|
|
|
|
|
|
|
public void close() {
|
2021-05-21 15:37:59 +02:00
|
|
|
for (int i = 0, len = smallSubpagePools.length; i < len; i++) {
|
|
|
|
PoolSubpage page = (PoolSubpage) SUBPAGE_ARRAY.getVolatile(smallSubpagePools, i);
|
|
|
|
if (page != null) {
|
|
|
|
page.destroy();
|
|
|
|
}
|
2021-05-12 10:44:33 +02:00
|
|
|
}
|
|
|
|
for (PoolChunkList list : new PoolChunkList[] {qInit, q000, q025, q050, q100}) {
|
|
|
|
list.destroy();
|
|
|
|
}
|
|
|
|
}
|
2021-05-11 11:24:06 +02:00
|
|
|
}
|