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

12

我认为这个问题之前应该已经有人问过了,但是我在这里没找到......

我使用SWIG创建了一个JNI包装器来封装一个C++类。所有功能都很完美,除了Java似乎从未调用该类的finalize()方法,因此我的类的析构函数也从未被调用。该类的析构函数执行一些最终的文件I/O操作,所以不幸的是,这不仅仅是一个小的内存泄漏问题。

通过谷歌搜索,似乎没有办法强制Java进行垃圾回收并销毁对象。是这样吗?

我知道我可以修改我的SWIG文件并创建一个Java函数来调用C++析构函数,但是该类被终端用户在多个不同的平台/语言中使用,因此添加一个仅适用于Java的函数将会产生不一致性,我们的技术作家不会喜欢。

5个回答

8

你不能强制执行System.gc()进行垃圾回收。 此外,不能保证一定会进行垃圾回收,例如如果你的应用程序只运行了很短的时间,然后finalizer根本不会运行(JVM在退出时不会运行它)。你应该为你的类创建一个close()或destroy()或其他函数,并在使用完该类的实例后调用它,最好从finally块中调用,如下所示:


MyClass x = null;
try{
    x = new MyClass();
    x.work();
} finally {
    if (x!=null)
        x.close();
}

7

在我看来,Java终结器大多数是无用的,当然也不能替代C++的析构函数。不幸的是,Java没有C++ RAII的替代品。

不要试图强制使用Java终结器。当你完成某个任务时,只需调用一个可以处理它的函数即可。这就是你能做的全部。


4
如果你依赖finalize方法中的代码在某个特定时间运行,你需要重新考虑你的方法。问题在于你不知道JVM何时调用finalize,因为你不知道对象何时被垃圾回收。
有一件事情你应该考虑,由于你的类将在其他项目中被重复使用,可能会出现终端用户以某种方式使用类的实例,使其不被回收或者垃圾回收不太可能,例如创建一个对该类实例的静态引用。我认为创建一个closedestroy方法是最安全的方法,以确保与你的Java对象相关联的C++类实例正在使用的资源被适当释放。
由于重用是一个问题,你可以让C++析构函数检查资源是否已经被释放,并在没有释放时调用相同的代码,如下所示:
class MyThing {
  public:
    void close();
    ~MyThing();

  private:
    bool released = false;
};

void
MyThing::close() {
  // close logic here
  this->released = true;
}

MyThing::~MyThing() {
  if (!released) {
    this->close();
  }
}

这样,您现有的C++代码就不需要太多更改,您可以确保通过JNI运行本地代码时资源以确定性方式释放。


2

在仔细查看SWIG生成的代码后,我发现SWIG开发人员实际上已经解决了这个问题 - 他们为您添加了一个delete()函数。它似乎已经很好地处理了程序员和GC同时删除对象的可能性。


-1

像这样的问题是C#选择IDisposable模式进行确定性终结的原因。

我建议您遵循相同的模式,并为您的Java用户进行适应。

在您的c++类中,创建一个单独的公共方法来处理您的资源。将其命名为close、dispose或其他名称。

让您的C++析构函数调用该公共方法,并告诉使用C++类的托管/GC用户必须调用该方法以避免内存泄漏。


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