Java直接内存:在自定义类中使用sun.misc.Cleaner

在Java中,由nio直接缓冲区分配的内存通过sun.misc.Cleaner实例释放,一些特殊的幻像引用比对象终结更有效。

这个更清洁的机制是仅在JVM中为直接缓冲区子类进行硬编码,还是可以在自定义组件中使用清理程序(例如编写自定义直接字节缓冲区)?

在这里,我不是在讨论检索现有nio直接缓冲区的清除字段。 我不是在谈论手动释放内存。 这是关于编写一个新类,它分配直接内存并由垃圾收集器机制有效地自动清理。

花了更多时间阅读API文档( http://docs.oracle.com/javase/7/docs/api/java/lang/ref/package-summary.html )后,我想我有一个更详细的答案:

1)可以重用sun.misc.Cleaner来执行自己的自定义类的高效清理。 您通过调用提供的工厂方法声明清理器:

sun.misc.Cleaner.create(Object ob, Runnable cleanup); 

有一段时间我无法让它正常工作,那是因为我足够愚蠢地将我的清洁工的可运行清理代码定义为一个匿名类,它保留了(强烈)引用我的指示对象,防止它永远“幻影可达“……

2)没有其他方法可以实现这种有效的清理(即使借助幻像引用也没有)

实际上,引用处理程序线程以一种特殊的方式处理sun.misc.Cleaner的实例:

 // Fast path for cleaners if (r instanceof Cleaner) { ((Cleaner)r).clean(); continue; } 

这意味着直接从引用处理程序线程调用清理代码,而在标准用法中,引用必须由引用处理程序线程入队,然后由另一个应用程序线程出列并处理。

希望它对你有帮助,如果使用java9。

下面的代码已在Intellij IDEA 2017和oracle jdk 9中测试过。

 import java.lang.ref.Cleaner; public class Main { public Main() { } public static void main(String[] args) { System.out.println("Hello World!"); while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Cleaner cleaner = Cleaner.create(); Main obj = new Main(); cleaner.register(obj, new Runnable() { @Override public void run() { System.out.println("Hello World!222"); } }); System.gc(); } } } 

如果你依赖sun.misc包中的任何东西,你就会冒着它消失和破坏代码的风险。 有些部分比其他部分更稳定,但它通常是一个坏主意(魔鬼的拥护者: sun.misc.Unsafe中的很多方法实际上是由JVM内在函数实现的,使得它们比用户编写的JNI代码更快)。

在这种情况下,我认为这是一个坏主意: Cleaner是通过PhantomReference实现清理的一种可能方式。 还有其他人; 谷歌的例子。 就此而言,您可以查看Cleaner本身的源代码 ,作为如何使用幻像引用的示例。

如果您要使用引用堆外对象的堆上对象, 需要某种清理处理程序。 否则,当收集堆栈对象时,您将创建真正的内存泄漏。