SWIG/Java: 对象过早被 GCed

SWIG/Java: Object gets GCed too early

我正在使用 SWIG 围绕一些 C++ 代码编写一个 Java 包装器。绑定现在可以使用,但不幸的是,我在本机端创建的对象释放得太快了。

代码从下到上:

C++: GitHub

// ...
base::Model *LoadVirtual(const char *file_name, const Config &config = Config(),
                         ModelType if_arpa = PROBING);
// ...

痛饮: GitHub

namespace lm::ngram {
    // ...
    lm::base::Model* LoadVirtual(const char *file_name,
                                 const lm::ngram::Config &config = lm::ngram::Config());
}

Java: GitHub

public class Model {
    private com.github.jbaiter.kenlm.jni.Model cModel;
    private String path;
    //...
    public Model(String path, Config config) throws ModelException {
        this.path = path;
        this.cModel = KenLM.LoadVirtual(path, config.getCConfig());
    }

    public long getOrder() {
        return this.cModel.Order();
    }
    //...
}

有问题的测试用例GitHub

@Test
public void getOrder() throws Exception {
    Model model = new Model(toy0Url.getPath());
    assertEquals(model.getOrder(), 3);
}

测试失败并抛出 NullPointerException,因为 model.cModelnull。但是,当我在 Model 构造函数中设置断点时,字段设置正确并且 getOrder returns 预期结果。但是,一旦离开构造函数的范围,我的 Model class 上的 所有字段 就会突然变成 nullcModelnull 可能是因为我对 SWIG 的内存管理缺乏了解,但 path 字段也是 null 的事实确实令人费解对我来说。

您可能正在创建新对象只是为了与 Java 端一起使用(本机端没有对该对象的引用)。 如果为真,那么您需要指定此方法创建将遵循 java lifetime/gc

的新对象
%newobject lm::base::Model* LoadVirtual(const char *file_name,
                             const lm::ngram::Config &config = lm::ngram::Config());

有关它的更多信息,您可以在 swig 文档中找到:http://www.swig.org/Doc1.3/Customization.html#ownership

根本原因实际上是我的 Java 代码中的一个错误,与 SWIG 本身无关。 我有两个 Model class 的构造函数,其中一个用 new Model(args) 而不是 this(args) 调用另一个。当然,这会立即丢弃正确初始化的实例并给我留下未初始化的对象(所有内容都是 null)。