This is an automated email from the ASF dual-hosted git repository.
jiangtian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new a23db285d89 Implememt auto release technique for Blob Allocator
(#15491)
a23db285d89 is described below
commit a23db285d89af55a9e35ca2cd37ed7ec1b3a11bc
Author: Potato <[email protected]>
AuthorDate: Wed May 14 10:35:07 2025 +0800
Implememt auto release technique for Blob Allocator (#15491)
* add auto release logic
* add license
* enhance
Signed-off-by: OneSizeFitQuorum <[email protected]>
* Refine API
Signed-off-by: OneSizeFitQuorum <[email protected]>
* fix typo
Signed-off-by: OneSizeFitQuorum <[email protected]>
---------
Signed-off-by: OneSizeFitQuorum <[email protected]>
Co-authored-by: MrQuansy <[email protected]>
---
.../commons/binaryallocator/BinaryAllocator.java | 66 +++++++++++++---
.../PooledBinaryPhantomReference.java | 42 ++++++++++
.../iotdb/commons/binaryallocator/arena/Arena.java | 36 +++++++--
.../binaryallocator/autoreleaser/Releaser.java | 91 ++++++++++++++++++++++
.../binaryallocator/config/AllocatorConfig.java | 4 +-
.../commons/binaryallocator/evictor/Evictor.java | 25 +++---
.../iotdb/commons/concurrent/ThreadName.java | 1 +
.../binaryallocator/BinaryAllocatorTest.java | 44 +++++++++--
8 files changed, 274 insertions(+), 35 deletions(-)
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocator.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocator.java
index ca676176a60..66c083c70c9 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocator.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocator.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.commons.binaryallocator;
import org.apache.iotdb.commons.binaryallocator.arena.Arena;
import org.apache.iotdb.commons.binaryallocator.arena.ArenaStrategy;
+import org.apache.iotdb.commons.binaryallocator.autoreleaser.Releaser;
import org.apache.iotdb.commons.binaryallocator.config.AllocatorConfig;
import org.apache.iotdb.commons.binaryallocator.evictor.Evictor;
import org.apache.iotdb.commons.binaryallocator.metric.BinaryAllocatorMetrics;
@@ -33,7 +34,11 @@ import org.apache.tsfile.utils.PooledBinary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.lang.ref.ReferenceQueue;
import java.time.Duration;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
public class BinaryAllocator {
@@ -49,14 +54,22 @@ public class BinaryAllocator {
private final BinaryAllocatorMetrics metrics;
private Evictor sampleEvictor;
+ private Releaser autoReleaser;
private static final ThreadLocal<ThreadArenaRegistry> arenaRegistry =
ThreadLocal.withInitial(ThreadArenaRegistry::new);
- private static final int WARNING_GC_TIME_PERCENTAGE = 10;
- private static final int HALF_GC_TIME_PERCENTAGE = 20;
+ private static final int WARNING_GC_TIME_PERCENTAGE = 20;
+ private static final int HALF_GC_TIME_PERCENTAGE = 25;
private static final int SHUTDOWN_GC_TIME_PERCENTAGE = 30;
private static final int RESTART_GC_TIME_PERCENTAGE = 5;
+ public final ReferenceQueue<PooledBinary> referenceQueue = new
ReferenceQueue<>();
+
+ // JDK 9+ Cleaner uses double-linked list and synchronized to manage
references, which has worse
+ // performance than lock-free hash set
+ public final Set<PooledBinaryPhantomReference> phantomRefs =
+ Collections.newSetFromMap(new ConcurrentHashMap<>());
+
public BinaryAllocator(AllocatorConfig allocatorConfig) {
this.allocatorConfig = allocatorConfig;
@@ -87,8 +100,14 @@ public class BinaryAllocator {
sampleEvictor =
new SampleEvictor(
ThreadName.BINARY_ALLOCATOR_SAMPLE_EVICTOR.getName(),
- allocatorConfig.durationEvictorShutdownTimeout);
- sampleEvictor.startEvictor(allocatorConfig.durationBetweenEvictorRuns);
+ allocatorConfig.durationShutdownTimeout,
+ allocatorConfig.durationBetweenEvictorRuns);
+ sampleEvictor.start();
+ autoReleaser =
+ new AutoReleaser(
+ ThreadName.BINARY_ALLOCATOR_AUTO_RELEASER.getName(),
+ allocatorConfig.durationShutdownTimeout);
+ autoReleaser.start();
}
public synchronized void close(boolean forceClose) {
@@ -99,13 +118,14 @@ public class BinaryAllocator {
state.set(BinaryAllocatorState.PENDING);
}
- sampleEvictor.stopEvictor();
+ sampleEvictor.stop();
+ autoReleaser.stop();
for (Arena arena : heapArenas) {
arena.close();
}
}
- public PooledBinary allocateBinary(int reqCapacity) {
+ public PooledBinary allocateBinary(int reqCapacity, boolean autoRelease) {
if (reqCapacity < allocatorConfig.minAllocateSize
|| reqCapacity > allocatorConfig.maxAllocateSize
|| state.get() != BinaryAllocatorState.OPEN) {
@@ -114,7 +134,7 @@ public class BinaryAllocator {
Arena arena = arenaStrategy.choose(heapArenas);
- return new PooledBinary(arena.allocate(reqCapacity), reqCapacity,
arena.getArenaID());
+ return arena.allocate(reqCapacity, autoRelease);
}
public void deallocateBinary(PooledBinary binary) {
@@ -125,7 +145,7 @@ public class BinaryAllocator {
int arenaIndex = binary.getArenaIndex();
if (arenaIndex != -1) {
Arena arena = heapArenas[arenaIndex];
- arena.deallocate(binary.getValues());
+ arena.deallocate(binary);
}
}
}
@@ -168,11 +188,13 @@ public class BinaryAllocator {
}
private static class BinaryAllocatorHolder {
+
private static final BinaryAllocator INSTANCE =
new BinaryAllocator(AllocatorConfig.DEFAULT_CONFIG);
}
private static class ThreadArenaRegistry {
+
private Arena threadArenaBinding = null;
public Arena getArena() {
@@ -199,6 +221,7 @@ public class BinaryAllocator {
}
private static class LeastUsedArenaStrategy implements ArenaStrategy {
+
@Override
public Arena choose(Arena[] arenas) {
Arena boundArena = arenaRegistry.get().getArena();
@@ -250,8 +273,9 @@ public class BinaryAllocator {
public class SampleEvictor extends Evictor {
- public SampleEvictor(String name, Duration evictorShutdownTimeoutDuration)
{
- super(name, evictorShutdownTimeoutDuration);
+ public SampleEvictor(
+ String name, Duration evictorShutdownTimeoutDuration, Duration
durationBetweenEvictorRuns) {
+ super(name, evictorShutdownTimeoutDuration, durationBetweenEvictorRuns);
}
@Override
@@ -263,4 +287,26 @@ public class BinaryAllocator {
metrics.updateSampleEvictionCounter(evictedSize);
}
}
+
+ /** Process phantomly reachable objects and return their byte arrays to
pool. */
+ public class AutoReleaser extends Releaser {
+
+ public AutoReleaser(String name, Duration shutdownTimeoutDuration) {
+ super(name, shutdownTimeoutDuration);
+ }
+
+ @Override
+ public void run() {
+ PooledBinaryPhantomReference ref;
+ try {
+ while ((ref = (PooledBinaryPhantomReference) referenceQueue.remove())
!= null) {
+ phantomRefs.remove(ref);
+ ref.slabRegion.deallocate(ref.byteArray);
+ }
+ } catch (InterruptedException e) {
+ LOGGER.info("{} exits due to interruptedException.", name);
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/PooledBinaryPhantomReference.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/PooledBinaryPhantomReference.java
new file mode 100644
index 00000000000..84541219c08
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/PooledBinaryPhantomReference.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.iotdb.commons.binaryallocator;
+
+import org.apache.iotdb.commons.binaryallocator.arena.Arena;
+
+import org.apache.tsfile.utils.PooledBinary;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+
+public class PooledBinaryPhantomReference extends
PhantomReference<PooledBinary> {
+ public final byte[] byteArray;
+ public Arena.SlabRegion slabRegion;
+
+ public PooledBinaryPhantomReference(
+ PooledBinary referent,
+ ReferenceQueue<? super PooledBinary> q,
+ byte[] byteArray,
+ Arena.SlabRegion region) {
+ super(referent, q);
+ this.byteArray = byteArray;
+ this.slabRegion = region;
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/arena/Arena.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/arena/Arena.java
index 9a38cb1fe58..1a8a85ec370 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/arena/Arena.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/arena/Arena.java
@@ -20,10 +20,15 @@
package org.apache.iotdb.commons.binaryallocator.arena;
import org.apache.iotdb.commons.binaryallocator.BinaryAllocator;
+import org.apache.iotdb.commons.binaryallocator.PooledBinaryPhantomReference;
import org.apache.iotdb.commons.binaryallocator.config.AllocatorConfig;
import org.apache.iotdb.commons.binaryallocator.ema.AdaptiveWeightedAverage;
import org.apache.iotdb.commons.binaryallocator.utils.SizeClasses;
+import org.apache.tsfile.utils.PooledBinary;
+
+import java.lang.ref.ReferenceQueue;
+import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
@@ -39,6 +44,9 @@ public class Arena {
private int sampleCount;
+ private final ReferenceQueue<PooledBinary> referenceQueue;
+ private final Set<PooledBinaryPhantomReference> phantomRefs;
+
public Arena(
BinaryAllocator allocator, SizeClasses sizeClasses, int id,
AllocatorConfig allocatorConfig) {
this.binaryAllocator = allocator;
@@ -52,20 +60,31 @@ public class Arena {
}
sampleCount = 0;
+ referenceQueue = binaryAllocator.referenceQueue;
+ phantomRefs = binaryAllocator.phantomRefs;
}
public int getArenaID() {
return arenaID;
}
- public byte[] allocate(int reqCapacity) {
+ public PooledBinary allocate(int reqCapacity, boolean autoRelease) {
final int sizeIdx = sizeClasses.size2SizeIdx(reqCapacity);
- return regions[sizeIdx].allocate();
+ byte[] data = regions[sizeIdx].allocate();
+ if (autoRelease) {
+ PooledBinary binary = new PooledBinary(data, reqCapacity, -1);
+ PooledBinaryPhantomReference ref =
+ new PooledBinaryPhantomReference(binary, referenceQueue, data,
regions[sizeIdx]);
+ phantomRefs.add(ref);
+ return binary;
+ } else {
+ return new PooledBinary(data, reqCapacity, arenaID);
+ }
}
- public void deallocate(byte[] bytes) {
- final int sizeIdx = sizeClasses.size2SizeIdx(bytes.length);
- regions[sizeIdx].deallocate(bytes);
+ public void deallocate(PooledBinary binary) {
+ final int sizeIdx = sizeClasses.size2SizeIdx(binary.getLength());
+ regions[sizeIdx].deallocate(binary.getValues());
}
public long evict(double ratio) {
@@ -146,8 +165,13 @@ public class Arena {
return evictedSize;
}
- private static class SlabRegion {
+ public static class SlabRegion {
private final int byteArraySize;
+
+ // Current implementation uses ConcurrentLinkedQueue for simplicity
+ // TODO: Can be optimized with more efficient lock-free approaches:
+ // 1. No need for strict FIFO, it's just an object pool
+ // 2. Use segmented arrays/queues with per-segment counters to reduce
contention
private final ConcurrentLinkedQueue<byte[]> queue;
private final AtomicInteger allocationsFromAllocator;
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/autoreleaser/Releaser.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/autoreleaser/Releaser.java
new file mode 100644
index 00000000000..87fd7f45f56
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/autoreleaser/Releaser.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.apache.iotdb.commons.binaryallocator.autoreleaser;
+
+import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+public abstract class Releaser implements Runnable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Releaser.class);
+
+ private Future<?> future;
+ protected final String name;
+ private final Duration shutdownTimeoutDuration;
+
+ private ExecutorService executor;
+
+ public Releaser(String name, Duration shutdownTimeoutDuration) {
+ this.name = name;
+ this.shutdownTimeoutDuration = shutdownTimeoutDuration;
+ }
+
+ /** Cancels the future. */
+ void cancel() {
+ future.cancel(false);
+ }
+
+ @Override
+ public abstract void run();
+
+ void setFuture(final Future<?> future) {
+ this.future = future;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + " [future=" + future + "]";
+ }
+
+ public void start() {
+ if (null == executor) {
+ executor = IoTDBThreadPoolFactory.newSingleThreadExecutor(name);
+ }
+ final Future<?> future = executor.submit(this);
+ this.setFuture(future);
+ }
+
+ public void stop() {
+ if (executor == null) {
+ return;
+ }
+
+ LOGGER.info("Stopping {}", name);
+
+ cancel();
+ executor.shutdown();
+ try {
+ boolean result =
+ executor.awaitTermination(shutdownTimeoutDuration.toMillis(),
TimeUnit.MILLISECONDS);
+ if (!result) {
+ LOGGER.info("unable to stop auto releaser after {} ms",
shutdownTimeoutDuration.toMillis());
+ }
+ } catch (final InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
+ executor = null;
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/config/AllocatorConfig.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/config/AllocatorConfig.java
index 53f20ac0da1..3bbbbc9a210 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/config/AllocatorConfig.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/config/AllocatorConfig.java
@@ -37,8 +37,8 @@ public class AllocatorConfig {
public boolean enableBinaryAllocator =
CommonDescriptor.getInstance().getConfig().isEnableBinaryAllocator();
- /** Maximum wait time in milliseconds when shutting down the evictor */
- public Duration durationEvictorShutdownTimeout = Duration.ofMillis(1000L);
+ /** Maximum wait time in milliseconds when shutting down the evictor and
autoReleaser */
+ public Duration durationShutdownTimeout = Duration.ofMillis(1000L);
/** Time interval in milliseconds between two consecutive evictor runs */
public Duration durationBetweenEvictorRuns = Duration.ofMillis(1000L);
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/evictor/Evictor.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/evictor/Evictor.java
index 686e7e73d8f..de1340db482 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/evictor/Evictor.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/binaryallocator/evictor/Evictor.java
@@ -35,13 +35,16 @@ public abstract class Evictor implements Runnable {
private ScheduledFuture<?> scheduledFuture;
private final String name;
- private final Duration evictorShutdownTimeoutDuration;
+ private final Duration shutdownTimeoutDuration;
+ private final Duration durationBetweenEvictorRuns;
private ScheduledExecutorService executor;
- public Evictor(String name, Duration evictorShutdownTimeoutDuration) {
+ public Evictor(
+ String name, Duration shutdownTimeoutDuration, Duration
durationBetweenEvictorRuns) {
this.name = name;
- this.evictorShutdownTimeoutDuration = evictorShutdownTimeoutDuration;
+ this.shutdownTimeoutDuration = shutdownTimeoutDuration;
+ this.durationBetweenEvictorRuns = durationBetweenEvictorRuns;
}
/** Cancels the scheduled future. */
@@ -61,17 +64,21 @@ public abstract class Evictor implements Runnable {
return getClass().getName() + " [scheduledFuture=" + scheduledFuture + "]";
}
- public void startEvictor(final Duration delay) {
+ public void start() {
if (null == executor) {
executor = IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(name);
}
final ScheduledFuture<?> scheduledFuture =
ScheduledExecutorUtil.safelyScheduleAtFixedRate(
- executor, this, delay.toMillis(), delay.toMillis(),
TimeUnit.MILLISECONDS);
+ executor,
+ this,
+ durationBetweenEvictorRuns.toMillis(),
+ durationBetweenEvictorRuns.toMillis(),
+ TimeUnit.MILLISECONDS);
this.setScheduledFuture(scheduledFuture);
}
- public void stopEvictor() {
+ public void stop() {
if (executor == null) {
return;
}
@@ -82,11 +89,9 @@ public abstract class Evictor implements Runnable {
executor.shutdown();
try {
boolean result =
- executor.awaitTermination(
- evictorShutdownTimeoutDuration.toMillis(),
TimeUnit.MILLISECONDS);
+ executor.awaitTermination(shutdownTimeoutDuration.toMillis(),
TimeUnit.MILLISECONDS);
if (!result) {
- LOGGER.info(
- "unable to stop evictor after {} ms",
evictorShutdownTimeoutDuration.toMillis());
+ LOGGER.info("unable to stop evictor after {} ms",
shutdownTimeoutDuration.toMillis());
}
} catch (final InterruptedException ignored) {
Thread.currentThread().interrupt();
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java
index dae0bd76581..e208f0eb87a 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java
@@ -193,6 +193,7 @@ public enum ThreadName {
STORAGE_ENGINE_RECOVER_TRIGGER("StorageEngine-RecoverTrigger"),
FILE_TIME_INDEX_RECORD("FileTimeIndexRecord"),
BINARY_ALLOCATOR_SAMPLE_EVICTOR("BinaryAllocator-SampleEvictor"),
+ BINARY_ALLOCATOR_AUTO_RELEASER("BinaryAllocator-Auto-Releaser"),
// the unknown thread name is used for metrics
UNKNOWN("UNKNOWN");
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocatorTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocatorTest.java
index 0fb4f0d96b5..27cb2d37cce 100644
---
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocatorTest.java
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/binaryallocator/BinaryAllocatorTest.java
@@ -29,12 +29,15 @@ import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class BinaryAllocatorTest {
+
@Test
public void testAllocateBinary() {
AllocatorConfig config = new AllocatorConfig();
@@ -42,19 +45,19 @@ public class BinaryAllocatorTest {
BinaryAllocator binaryAllocator = new BinaryAllocator(config);
binaryAllocator.resetArenaBinding();
- PooledBinary binary = binaryAllocator.allocateBinary(255);
+ PooledBinary binary = binaryAllocator.allocateBinary(255, false);
assertNotNull(binary);
assertEquals(binary.getArenaIndex(), -1);
assertEquals(binary.getLength(), 255);
binaryAllocator.deallocateBinary(binary);
- binary = binaryAllocator.allocateBinary(65536);
+ binary = binaryAllocator.allocateBinary(65536, false);
assertNotNull(binary);
assertEquals(binary.getArenaIndex(), 0);
assertEquals(binary.getLength(), 65536);
binaryAllocator.deallocateBinary(binary);
- binary = binaryAllocator.allocateBinary(65535);
+ binary = binaryAllocator.allocateBinary(65535, false);
assertNotNull(binary);
assertEquals(binary.getArenaIndex(), 0);
assertEquals(binary.getLength(), 65535);
@@ -67,8 +70,8 @@ public class BinaryAllocatorTest {
BinaryAllocator binaryAllocator = new
BinaryAllocator(AllocatorConfig.DEFAULT_CONFIG);
binaryAllocator.resetArenaBinding();
- PooledBinary binary1 = binaryAllocator.allocateBinary(4096);
- PooledBinary binary2 = binaryAllocator.allocateBinary(4096);
+ PooledBinary binary1 = binaryAllocator.allocateBinary(4096, false);
+ PooledBinary binary2 = binaryAllocator.allocateBinary(4096, false);
assertEquals(binary1.getArenaIndex(), binary2.getArenaIndex());
binaryAllocator.deallocateBinary(binary1);
binaryAllocator.deallocateBinary(binary2);
@@ -81,7 +84,7 @@ public class BinaryAllocatorTest {
new Thread(
() -> {
try {
- PooledBinary firstBinary =
binaryAllocator.allocateBinary(2048);
+ PooledBinary firstBinary =
binaryAllocator.allocateBinary(2048, false);
int arenaId = firstBinary.getArenaIndex();
arenaUsageCount.merge(arenaId, 1, Integer::sum);
binaryAllocator.deallocateBinary(firstBinary);
@@ -107,7 +110,7 @@ public class BinaryAllocatorTest {
BinaryAllocator binaryAllocator = new BinaryAllocator(config);
binaryAllocator.resetArenaBinding();
- PooledBinary binary = binaryAllocator.allocateBinary(4096);
+ PooledBinary binary = binaryAllocator.allocateBinary(4096, false);
binaryAllocator.deallocateBinary(binary);
assertEquals(binaryAllocator.getTotalUsedMemory(), 4096);
Thread.sleep(200);
@@ -136,4 +139,31 @@ public class BinaryAllocatorTest {
}
}
}
+
+ @Test
+ public void testAutoRelease() throws InterruptedException {
+ AllocatorConfig config = new AllocatorConfig();
+ config.minAllocateSize = 4096;
+ config.maxAllocateSize = 65536;
+ BinaryAllocator binaryAllocator = new BinaryAllocator(config);
+ binaryAllocator.resetArenaBinding();
+
+ PooledBinary binary = binaryAllocator.allocateBinary(4096, true);
+ assertNotNull(binary);
+ assertEquals(binary.getArenaIndex(), -1);
+ assertEquals(binary.getLength(), 4096);
+ assertEquals(binaryAllocator.getTotalUsedMemory(), 0);
+
+ // reference count is 0
+ binary = null;
+ System.gc();
+ long startTime = System.currentTimeMillis();
+ while (System.currentTimeMillis() - startTime <=
TimeUnit.MINUTES.toMillis(1)) {
+ if (binaryAllocator.getTotalUsedMemory() == 4096) {
+ return;
+ }
+ Thread.sleep(100);
+ }
+ fail("Can not auto release PoolBinary in binary allocator");
+ }
}