11

我以为以前会问过这个问题,但我在这里找不到...

我使用 SWIG 围绕 C++ 类创建了一个 JNI 包装器。一切都很好,除了 Java 似乎从不调用类的 finalize(),因此,反过来,我的类的析构函数永远不会被调用。该类的析构函数执行一些最终文件 I/O,因此不幸的是,这不仅仅是轻微的内存泄漏。

通过谷歌搜索,似乎没有办法强制 Java 进行 GC 并销毁对象。真的?

我知道我可以操纵我的 SWIG 文件并创建一个调用 C++ 析构函数的 java 函数,但是这个类被最终用户在几种不同的平台/语言中使用,所以添加一个纯 Java 会造成我们的技术不一致作家们不会喜欢的。

4

5 回答 5

8

您不能使用 System.gc() 强制 GC。此外,如果您的应用程序仅运行很短的时间然后您的终结器根本不会运行(JVM 在退出时不会运行它),也不能保证会运行 GC。您应该为您的类创建一个 close() 或 destroy() 或任何函数,并在使用完此类的实例后调用它,最好从 finally 块中调用它,例如。


MyClass x = null;
try{
    x = new MyClass();
    x.work();
} finally {
    if (x!=null)
        x.close();
}
于 2009-06-18T19:50:57.483 回答
7

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

不要费心试图强制 Java 完成。当您完成任何事情时,所有功能都会处理它。这就是你所能做的。

于 2009-06-18T19:45:36.573 回答
4

如果您依赖finalize方法中的代码在某个时间运行,则需要重新考虑您的方法。这里的问题是你不知道finalizeJVM 什么时候会调用,因为你不知道对象什么时候会被垃圾回收。

由于您的类将在其他项目中重用,因此您应该考虑的一件事是,最终用户可能会以不会被收集或垃圾收集的方式使用类的实例不太可能,例如创建对类实例的静态引用。我认为创建closeordestroy方法是确保与您的 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 运行的本机代码的上下文中以确定性方式释放资源。

于 2009-06-18T21:15:26.017 回答
2

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

于 2009-06-19T13:15:00.557 回答
-1

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

我建议您遵循相同的模式并针对您的 Java 用户进行调整。

在您的 c++ 类中,创建一个单独的公共方法来处理您的资源。称它为关闭,或处置,或其他东西。

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

于 2009-06-18T20:02:50.220 回答