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:
parent
ab45a7b053
commit
987a398700
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user