Java的垃圾收集器过早地释放了SWIG结构成员

我有一个由
Java通过基于SWIG的接口调用的C库.在Java方面,我使用默认的struct接口和carrays.i的%array_class构建了一个包含指向其他结构数组的指针的结构.

因为Java的垃圾收集器不知道顶级结构的成员,所以有时释放该数组,其终结器delete []是其后备内存.我需要解决这个问题,最好不要在Java中复制结构,因为它相当大.

一个最小的例子看起来像这样(虽然它可能不会触发bug,因为它没有做太多):

C / SWIG:

%module example

%include "carrays.i"
%array_class(object, objectArray);

struct object {
    unsigned int id;
    char *name;
};

struct data {
    size_t nobjects;
    object *objects;
};

void use_data(data*);

Java的:

public class Example {
    private static data writeData() {
        data d = new data();
        objectArray os = new objectArray(3);
        for (int i = 0; i < 3; i++) {
            object o = new object();
            o.setId(i);
            o.setName("obj" + i);
            os.setitem(i, o);
        }
        d.setNobjects(3);
        d.setObjects(os.cast());

        return d;
    }

    public static void main(String[] args) {
        data d = writeData();
        example.use_data(d);
    }
}

最佳答案 这有几个可能的解决方案.最简单的是包装一个可以在没有Java“拥有”内存的情况下创建对象的函数.这看起来像是这样的:

%inline %{
object *new_object() {
  // SWIG will assume that it doesn't own this
  return new object;
}
%}

实际上,您可以在创建后修改swigCMemOwn布尔值.类型映射应该能够在适当的位置注入它(当对象被传递给setitem时).例如,您可以写:

%typemap(javacode) object %{
  object transfer() {
    swigCMemOwn = false;
    return this;
  }
%}

这需要在首次看到对象类之前,并允许您编写如下内容:

os.setitem(i, o.transfer());

而不是os.setitem(i,o);.

这个主题的变体是使用javain类型映射替换Java代理中的默认setitem实现,以便它调用对象上的函数(您提供)来更改所有权,例如:

%javamethodmodifiers objectArray::setitem "protected";
%rename  objectArray::setitem setitemImpl;

%typemap(javacode) objectArray %{
  public void setitem(int i, object o) {
    o.disown();
    setitemImpl(i, o);
  }
%}

之前%包含“carrays.i”和相应的disown()通过%typemap(javacode)对象. (swigCMemOwn受保护,因此objectArray无法直接修改它).

还有其他方法可能,也涉及设置所有权.我展示的那些是我认为最简单的.

或者,通过将其分配给成员变量,另一个常见的工作是keep a reference to the Java proxy Object hanging around.在这种情况下,因为你可能有很多对象,但它们必须是一个容器本身.

点赞