JNI:正确管理Java对象的生命周期

3
当我在c++中创建本地实现对等体时,如何确保当JVM删除java对象时也删除本地部分?我可以添加一些方法,用户必须显式调用,但我想知道是否有一些钩子可以处理java对象被删除(垃圾回收)的情况,以便我也可以自动删除c++实现对象。
我审查了JACE,似乎它可以做到这一点,但我需要运行PeerEnhancer来修补生成的类文件(可能是它如何挂接删除?或者可能它需要这种修补来做其他事情)。但是,我想避免与编译的java文件混淆,我不想要任何花哨的东西。
2个回答

4
请注意,采用终结器会在最糟糕的地方挑战JVM。在finalizer()中调用JNI就像在行驶时调整发动机气门定时一样。虽然从技术上讲是可能的,但很容易导致JVM泄漏或崩溃。一旦涉及到多线程,你肯定会出现死锁/崩溃。如果你说“我不想要任何花哨的东西”,我建议使用显式调用方法。是的,根据传统要求特定的方法顺序是非常糟糕的设计,但JNI本身就很糟糕。
如果一个受人尊敬的JNI库进行了隐蔽的类仪表化而不是简单地使用终结器,它可能有其原因。一些推荐阅读(甚至没有提到JNI!):

http://asserttrue.blogspot.com/2008/11/finalization-is-evil.html

http://elliottback.com/wp/java-memory-leaks-w-finalize-examples/

我的建议是:实现一个带有虚拟方法detachFromPeer()的基类,在其中设置一些“已分离”标志,然后在终结器中仅检查该标志并发出警告。

我知道GC问题。代码显然需要手动清理,但同时我的代码想要知道GC何时删除对象。 - Pavel P
你想知道垃圾回收器何时删除对象,还是程序代码、算法何时决定不再需要该对象?后者是确定性的和可管理的。相反,你无法知道垃圾回收器何时调用终结器,如果有的话。 - Pavel Zdenek
抱歉,问题的标题可能有误导性。我想知道的只是如何挂钩当垃圾回收实际丢弃对象的事件,而不是何时/为什么/如何丢弃。我没有任何依赖该挂钩的意图,我主要是用于调试(例如,断言手动清理已经执行等)。 - Pavel P
请注意,问题本身是误导性的。您明确要求“当Java对象被删除时,我可以自动删除C ++对象”。这听起来严重依赖于GC终结器钩子。无论如何,我很高兴在终结器中进行调试/断言手动清理是您想要做的最多的事情。您已经为自己节省了一些严重的未来痛苦。 - Pavel Zdenek
这里有一些FUD。Finalizers只在单个线程上调用,因此除非它们自己启动线程,否则肯定不会出现死锁问题。 - user207421
终结器确实在单独的线程上调用,但是否在单个线程上是有争议的。JVM规范并不太确定。即使如此,只要程序员忘记格外小心并开始“优化”C++方面的内容,整个过程就只能工作到某个时候了。一旦在C++处理过程中尝试访问某些共享资源,那么终结器从单独的线程运行这个事实将会伤害你。 - Pavel Zdenek

2

这是一个很少见的情况,你应该真正使用finalizer,但你也应该使你的Java类成为Closeable。


我不是Java专家,我会把这个问题传给我的Java朋友们 :) 是否可以将那些样板代码放在一个基类中,以便所有需要本地对等体的Java类都必须继承自该处理自动化事务的类(例如,在Java析构函数/终结器中调用虚拟的detachFromPeer()方法,以确保C++对象没有无故悬挂)。 - Pavel P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接