凭借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?
答案 0 :(得分:5)
让我们考虑为什么最终确定和Co有问题的原因:正如您所知,无法保证在VM关闭之前调用finalize,这意味着特殊的清理代码不一定会运行(这是一个错误的决定,我没有看到在清理时运行finalize队列有任何问题,但这就是它的方式)。这也与C#
中的情况完全相同现在你的对象只消耗内存,当虚拟机被破坏时,无论如何都会由操作系统清理内存,因此唯一一个有问题的情况对你来说无关紧要。所以,是的,你确实可以使用这个变体,它可以完美地工作,但它可能不完全被认为是一个伟大的架构设计 - 并且只要你的C ++代码添加资源,操作系统无法正确处理清理你会遇到问题
另请注意,实现终结器会导致GC的额外开销,并且意味着需要两个周期来清理其中一个对象(无论你做什么,都不要在finalize方法中保存对象)
答案 1 :(得分:2)
如果您了解为什么要避免使用Java的finalize方法,那么您也将了解如何正确使用它。使用finalize关闭系统资源(文件和句柄)是不好的,因为您实际上并不知道何时关闭和释放这些资源。使用复杂的终结逻辑很糟糕,因为您的对象引用可能会泄漏并再次被固定在内存中。
对于您的场景,使用finalize非常好。
答案 2 :(得分:1)
使用带有终结器的包装器是一个不错的解决方案
但是如果你真的不想这样做,你可以使用带有ReferenceQueue的PhantomReference来清理它(但是你需要一个单独的线程来轮询队列)
答案 3 :(得分:0)
那么我们如何使用幻像参考来实现它。