Fix native memory leak bug

Motivation:
The untethered memory allocated by ensureWritable in a direct MemorySegment based non-pooled Buffer would be allocated without having a Cleaner attached to its ResourceScope.
This could cause that memory to leak if the Buffer instance was cast aside.

Modification:
ManagedBufferAllocator now makes sure to attach a cleaner to the buffer and its memory segment, when allocating untethered memory.

Result:
The BufferTest$CleanerTests now pass.
This commit is contained in:
Chris Vest 2021-04-07 17:17:35 +02:00
parent ab45a7b053
commit 987a398700
3 changed files with 24 additions and 4 deletions

View File

@ -37,7 +37,7 @@ class ManagedBufferAllocator implements BufferAllocator, AllocatorControl {
@Override
public Object allocateUntethered(Buffer originator, int size) {
BufferAllocator.checkSize(size);
var buf = manager.allocateShared(this, size, NO_OP_DROP, null);
var buf = manager.allocateShared(this, size, NO_OP_DROP, cleaner);
return manager.unwrapRecoverableMemory(buf);
}

View File

@ -21,15 +21,35 @@ import jdk.incubator.foreign.ResourceScope;
import java.lang.ref.Cleaner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Function;
import static jdk.incubator.foreign.ResourceScope.newSharedScope;
public class NativeMemorySegmentManager extends AbstractMemorySegmentManager {
public static final LongAdder MEM_USAGE_NATIVE = new LongAdder();
private static final ConcurrentHashMap<Long, Runnable> CLEANUP_ACTIONS = new ConcurrentHashMap<>();
private static final Function<Long, Runnable> CLEANUP_ACTION_MAKER = s -> new ReduceNativeMemoryUsage(s);
static Runnable getCleanupAction(long size) {
return CLEANUP_ACTIONS.computeIfAbsent(size, s -> () -> MEM_USAGE_NATIVE.add(-s));
return CLEANUP_ACTIONS.computeIfAbsent(size, CLEANUP_ACTION_MAKER);
}
private static final class ReduceNativeMemoryUsage implements Runnable {
private final long size;
private ReduceNativeMemoryUsage(long size) {
this.size = size;
}
@Override
public void run() {
MEM_USAGE_NATIVE.add(-size);
}
@Override
public String toString() {
return "ReduceNativeMemoryUsage(by " + size + " bytes)";
}
}
@Override

View File

@ -1683,8 +1683,8 @@ public class BufferTest {
for (int i = 0; i < iterations; i++) {
allocateAndForget(allocator, allocationSize);
System.gc();
System.runFinalization();
}
System.runFinalization();
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
var totalAllocated = (long) allocationSize * iterations;
assertThat(sum).isLessThan(totalAllocated);
@ -1705,8 +1705,8 @@ public class BufferTest {
for (int i = 0; i < iterations; i++) {
allocateAndForget(allocator, allocationSize);
System.gc();
System.runFinalization();
}
System.runFinalization();
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
var totalAllocated = (long) allocationSize * iterations;
assertThat(sum).isLessThan(totalAllocated);