本地C++库 - 谁需要释放内存,如何释放?

6
这段是关于Android的。情况如下:
C++库和Java包装类再加上本地函数(JNI),用于处理来自库中的C++类。当通用Java代码需要C++对象时,它会创建相应的Java包装器对象,该对象通过本地函数创建C++对象,并将指针记入“long”变量。在所有后续操作中,包装器将此指针传递给本地函数等。
问题是什么:
如何在结束时释放所有分配的C++对象?目前,每个包装类都有一个“finalize”方法,在其中调用本地函数以释放C++对象,但Android不能保证调用“finalize”!另一方面,通常C++库不知道Java代码分配了多少个和哪些类型的C++对象。
当我们的Java应用程序终止时,剩余的已分配内存会发生什么情况,当操作系统卸载库时,Android会自动释放使用本地库的整个堆吗?

你会在这里找到你需要的答案:https://dev59.com/anE95IYBdhLWcg3wAo9U - JACH
2
讨论的实质是“finalize不确定”,这一点我在我的问题中指出了。@rsp的提议仅适用于简单情况——当整个对象的生命周期位于连续的代码片段中时。今天的事件驱动编程通常包含更复杂的场景。 - ggurov
3个回答

3
在进程生命周期结束时,所有进程内存(包括Java和C++堆)将被系统释放和回收。但是需要注意的是,Android活动关闭并不一定意味着进程已经结束。我不确定那里的进程关闭策略是什么。
另一方面,依赖垃圾回收和finalize()听起来对我来说是很好的设计。你声称“Android不能保证finalize()”。你有这方面的引用吗?因为如果它附带一个免责声明:“当对象作为进程关闭的一部分被释放时...”,那么我们仍然没问题。
如果你非常谨慎,你可以编写自己的malloc()/free()/realloc()包装器,存储所有分配对象的列表,并引入一个清理函数,遍历该列表并释放它们。然而,包含Java对象的情况可能会处于奇怪的僵尸状态,其中内存已从它们下面释放。这是一个非常容易出错的棘手命题。所以我仍然会说 - 相信垃圾回收器。缺乏垃圾回收将会......令人不安。

在进程生命周期结束时,所有进程内存(包括Java和C++堆)将被系统释放和回收。如果这是确定的,那对我来说就足够了。Android活动关闭并不一定意味着进程结束。需要在主要活动完成后一段时间,或者可以通过System.exit或android.os.Process.killProcess强制结束。你有引用吗?不完全有,但这是我从引用链接和之前阅读过的其他链接中得出的印象。实际上,我进行了测试,在终结器中放置Log.w - 在进程死亡之前或之后没有任何消息。 - ggurov
如果你非常谨慎,你可以编写自己的...- 是的,我考虑过类似的事情。开始在JNI_OnUnload()上进行清理。不幸的是,JNI_OnUnload从未被调用过。如果确保系统会在库卸载时释放所有内容,那么就没有必要浪费时间编写这样的清理程序了。 - ggurov

1
由于范式的差异,您必须将显式销毁纳入到使用C++资源实现的Java对象中。因此需要一个close()或其他类似的方法。JNI也会遇到同样的问题,因此那些问题的答案也适用于您:

强制Java调用我的C++析构函数(JNI)

至于关闭时的内存问题,我认为最好不要依赖它。如果您达到了干净的状态,valgrind等工具可以确保您没有泄漏。
但从技术角度来看,由于Android基于Linux,我想它会像通常一样在进程关闭时释放所有内存。利用这一点可以使您的程序比显式释放内存更快地退出(仅限于专家使用其他方法确保程序正确性并且在运行时没有泄漏)。

关于你的链接,可以看看我对JACH的回答,问题是一样的。C++对象的某些包装可能深藏在程序对象的层次结构中,而关于这个层次结构的顶层,没有人会知道它的存在。"我想它会像通常一样,在进程关闭时释放所有内存" - 我希望如此。 - ggurov

0

我们正在使用JNIs,遇到了这样的问题

实际上,问题在于我们过载了finalize()来进行清理。我们通过删除finalize()并创建一个clean()来解决了这个问题。这个clean()调用JNI函数来执行适当的删除操作(并将C++指针设置为null,以防万一)。我们像在C++中使用delete一样,在变量超出范围时调用clean()。

这对我们很有效。希望对你也有帮助。祝好运!


几乎每个人都支持这种方法,但是想象一下:在Java对象X中的C++指针,它位于对象Y中,而对象Y又位于Z中。您可以在X中创建clean()方法,但是您还需要在Y和Z中定义此类方法!也就是说,为了在整个Java程序中模拟C++析构函数样式,您需要为每种情况都定义方法。我更喜欢认为Android将释放整个堆,这是我们必须期望的正常行为。 - ggurov

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