是否可以让GC管理本机对象的生命周期?

时间:2011-07-15 22:58:10

标签: java memory-management garbage-collection java-native-interface native

凭借C ++和C#经验以及一些小知识,我现在正在开始一个Java + JNI(C ++)项目(Android,如果这很重要)。

我有一个本机方法,它创建一些C ++类并返回一个指向它的指针作为Java long值(比如句柄)。然后在其中使用Java代码调用其他本机方法,使用句柄作为参数在此类上执行一些本机操作。 C ++方面不拥有该对象,它是Java方面的人。但是在当前的架构设计中,很难定义谁拥有对象以及何时删除它。因此,使Java VM垃圾收集器以某种方式管理对象的生命周期可能会很好。 C ++类不消耗任何资源,除了一些内存,不是很大。如果几个这样的物体不会被破坏,那就没关系了。

在C#中,我可能会在一些托管包装类中包装本机IntPtr句柄。并且当托管包装器被垃圾收集时,覆盖它的终结器来调用本机对象的析构函数。 SafeHandle,AddMemoryPressure等也可能对此有所帮助。

这与Java的最终版本不同。你在Java中的'Hello world'之后知道的第二件事是使用finalize是不好的。有没有其他方法可以在Java中实现这一目标?也许使用PhantomReference?

4 个答案:

答案 0 :(得分:5)

让我们考虑为什么最终确定和Co有问题的原因:正如您所知,无法保证在VM关闭之前调用finalize,这意味着特殊的清理代码不一定会运行(这是一个错误的决定,我没有看到在清理时运行finalize队列有任何问题,但这就是它的方式)。这也与C#

中的情况完全相同

现在你的对象只消耗内存,当虚拟机被破坏时,无论如何都会由操作系统清理内存,因此唯一一个有问题的情况对你来说无关紧要。所以,是的,你确实可以使用这个变体,它可以完美地工作,但它可能不完全被认为是一个伟大的架构设计 - 并且只要你的C ++代码添加资源,操作系统无法正确处理清理你会遇到问题

另请注意,实现终结器会导致GC的额外开销,并且意味着需要两个周期来清理其中一个对象(无论你做什么,都不要在finalize方法中保存对象)

答案 1 :(得分:2)

如果您了解为什么要避免使用Java的finalize方法,那么您也将了解如何正确使用它。使用finalize关闭系统资源(文件和句柄)是不好的,因为您实际上并不知道何时关闭和释放这些资源。使用复杂的终结逻辑很糟糕,因为您的对象引用可能会泄漏并再次被固定在内存中。

对于您的场景,使用finalize非常好。

答案 2 :(得分:1)

使用带有终结器的包装器是一个不错的解决方案

但是如果你真的不想这样做,你可以使用带有ReferenceQueue的PhantomReference来清理它(但是你需要一个单独的线程来轮询队列)

答案 3 :(得分:0)

那么我们如何使用幻像参考来实现它。

  1. 为本机intPtr对象创建包装器对象。创建一个 包装器对象上的幻像引用(带引用队列)。
  2. 创建并维护一个虚拟参考intPtr。
  3. 的地图
  4. 创建一个将监视已完成的引用队列的线程 包装器对象实例。
  5. 此线程将从引用队列获取幻像引用,使用幻像引用查找intPtr并在intPtr引用的本机int对象上调用析构函数。
  6. 虽然发生了这一切,但你可以快乐地使用 你的java代码中的包装器对象。