强制释放本机内存的示例直接ByteBuffer使用sun.misc.Unsafe分配?

JDK提供了分配所谓的直接ByteBuffers的能力,其中内存在Java堆之外分配。 这可能是有益的,因为垃圾收集器不会触及这个内存,因此不会导致GC开销:这对于像缓存这样的长期存在的东西来说非常有用。

但是,现有实现存在一个关键问题:当拥有ByteBuffer被垃圾收集时,底层内存只是异步分配; 没有办法强迫早期解除分配。 这可能会有问题,因为GC循环本身不受ByteBuffers处理的影响,并且假设ByteBuffers可能驻留在Old Generation内存区域,则可能在ByteBuffer不再使用后几小时调用GC。

但理论上应该可以直接使用sun.misc.Unsafe方法(freeMemory,allocateMemory):这是JDK本身用于分配/解除分配本机内存的方法。 看看代码,我看到的一个潜在问题是内存双重释放的可能性 – 所以我想确保正确清理状态。

任何人都可以指向我这样做的代码吗? 理想情况下,想要使用它而不是JNA。

注意:我看到这个问题有点相关。

看起来指出的答案是很好的方法: 这里是使用这个想法的Elastic Search的代码示例。 谢谢大家!

使用sun.misc.Unsafe几乎是不可能的,因为分配的本机内存的基地址是java.nio.DirectByteBuffer构造函数的局部变量。

实际上,您可以使用以下代码强制释放本机内存:

 import sun.misc.Cleaner; import java.lang.reflect.Field; import java.nio.ByteBuffer; ... public static void main(String[] args) throws Exception { ByteBuffer direct = ByteBuffer.allocateDirect(1024); Field cleanerField = direct.getClass().getDeclaredField("cleaner"); cleanerField.setAccessible(true); Cleaner cleaner = (Cleaner) cleanerField.get(direct); cleaner.clean(); } 

有一种更简单的清理内存的方法。

 public static void clean(ByteBuffer bb) { if(bb == null) return; Cleaner cleaner = ((DirectBuffer) bb).cleaner(); if (cleaner != null) cleaner.clean(); } 

如果您相当快地丢弃直接或内存映射的ByteBuffer,使用它可能会产生很大的不同。

使用Cleaner执行此操作的原因之一是您可以拥有底层内存资源的多个副本,例如slice()。 清洁工具有这些资源数量。

基本上你想要的是遵循与使用IO流相同的语义。 就像你需要关闭一次流,你需要释放一次内存。 因此,您可以围绕本机调用编写自己的包装器,从而尽早释放内存