Add a BBuf allocator for direct memory, that is also managed by a Cleaner

Motivation:
 Client code sometimes struggle with managing their buffer reference counts correctly,
 so there's a use case for buffers that are deallocated by cleaners.

Modification:
 Add another allocator, directWithCleaner, which registers all the native memory segments with cleaners.

Result:
 It's possible to get unpooled direct buffers, that are automatically deallocated by a cleaner if they are no longer strongly
 referenced.
 The same is not necessary for heap buffers, since they can be garbage collected like normal objects.
This commit is contained in:
Chris Vest 2020-09-25 16:56:03 +02:00
parent 1aa439991a
commit 1f4e33b42a
3 changed files with 46 additions and 21 deletions

View File

@ -74,6 +74,18 @@ public interface Allocator extends AutoCloseable {
};
}
static Allocator directWithCleaner() {
return new Allocator() {
@Override
public Buf allocate(long size) {
checkSize(size);
var segment = allocateNative(size);
segment.registerCleaner(Statics.CLEANER);
return new BBuf(segment, SEGMENT_CLOSE);
}
};
}
static Allocator pooledHeap() {
return new SizeClassedMemoryPool() {
@Override

View File

@ -0,0 +1,33 @@
package io.netty.buffer.b2;
import org.junit.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class DirectBBufWithCleanerTest extends DirectBBufTest {
@Override
protected Allocator createAllocator() {
return Allocator.directWithCleaner();
}
@Test
public void bufferMustBeClosedByCleaner() throws InterruptedException {
var allocator = createAllocator();
allocator.close();
int iterations = 100;
int allocationSize = 1024;
for (int i = 0; i < iterations; i++) {
allocateAndForget(allocator, allocationSize);
System.gc();
System.runFinalization();
}
var sum = Statics.MEM_USAGE_NATIVE.sum();
var totalAllocated = (long) allocationSize * iterations;
assertThat(sum, lessThan(totalAllocated));
}
protected void allocateAndForget(Allocator allocator, long size) {
allocator.allocate(size);
}
}

View File

@ -20,28 +20,12 @@ import org.junit.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class PooledDirectBBufWithCleanerTest extends DirectBBufTest {
public class PooledDirectBBufWithCleanerTest extends DirectBBufWithCleanerTest {
@Override
protected Allocator createAllocator() {
return Allocator.pooledDirectWithCleaner();
}
@Test
public void bufferMustBeClosedByCleaner() throws InterruptedException {
var allocator = createAllocator();
allocator.close();
int iterations = 100;
int allocationSize = 1024;
for (int i = 0; i < iterations; i++) {
allocateAndForget(allocator, allocationSize);
System.gc();
System.runFinalization();
}
var sum = Statics.MEM_USAGE_NATIVE.sum();
var totalAllocated = (long) allocationSize * iterations;
assertThat(sum, lessThan(totalAllocated));
}
@Test
public void buffersMustBeReusedByPoolingAllocatorEvenWhenDroppedByCleanerInsteadOfExplicitly()
throws InterruptedException {
@ -58,8 +42,4 @@ public class PooledDirectBBufWithCleanerTest extends DirectBBufTest {
assertThat(sum, lessThan(totalAllocated));
}
}
protected void allocateAndForget(Allocator allocator, long size) {
allocator.allocate(size);
}
}