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
|
@Override
|
||||||
public Object allocateUntethered(Buffer originator, int size) {
|
public Object allocateUntethered(Buffer originator, int size) {
|
||||||
BufferAllocator.checkSize(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);
|
return manager.unwrapRecoverableMemory(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,15 +21,35 @@ import jdk.incubator.foreign.ResourceScope;
|
|||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.LongAdder;
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static jdk.incubator.foreign.ResourceScope.newSharedScope;
|
import static jdk.incubator.foreign.ResourceScope.newSharedScope;
|
||||||
|
|
||||||
public class NativeMemorySegmentManager extends AbstractMemorySegmentManager {
|
public class NativeMemorySegmentManager extends AbstractMemorySegmentManager {
|
||||||
public static final LongAdder MEM_USAGE_NATIVE = new LongAdder();
|
public static final LongAdder MEM_USAGE_NATIVE = new LongAdder();
|
||||||
private static final ConcurrentHashMap<Long, Runnable> CLEANUP_ACTIONS = new ConcurrentHashMap<>();
|
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) {
|
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
|
@Override
|
||||||
|
@ -1683,8 +1683,8 @@ public class BufferTest {
|
|||||||
for (int i = 0; i < iterations; i++) {
|
for (int i = 0; i < iterations; i++) {
|
||||||
allocateAndForget(allocator, allocationSize);
|
allocateAndForget(allocator, allocationSize);
|
||||||
System.gc();
|
System.gc();
|
||||||
System.runFinalization();
|
|
||||||
}
|
}
|
||||||
|
System.runFinalization();
|
||||||
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
|
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
|
||||||
var totalAllocated = (long) allocationSize * iterations;
|
var totalAllocated = (long) allocationSize * iterations;
|
||||||
assertThat(sum).isLessThan(totalAllocated);
|
assertThat(sum).isLessThan(totalAllocated);
|
||||||
@ -1705,8 +1705,8 @@ public class BufferTest {
|
|||||||
for (int i = 0; i < iterations; i++) {
|
for (int i = 0; i < iterations; i++) {
|
||||||
allocateAndForget(allocator, allocationSize);
|
allocateAndForget(allocator, allocationSize);
|
||||||
System.gc();
|
System.gc();
|
||||||
System.runFinalization();
|
|
||||||
}
|
}
|
||||||
|
System.runFinalization();
|
||||||
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
|
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
|
||||||
var totalAllocated = (long) allocationSize * iterations;
|
var totalAllocated = (long) allocationSize * iterations;
|
||||||
assertThat(sum).isLessThan(totalAllocated);
|
assertThat(sum).isLessThan(totalAllocated);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user