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

发布于 2025-01-01 06:45:14 字数 1087 浏览 3 评论 0原文

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

由于 Java 的垃圾收集器不知道顶级结构的成员,因此有时会释放数组,此时其终结器将删除其后备内存。我需要一种解决方法,最好不要在 Java 中复制该结构,因为它相当大。

一个最小的示例如下所示(尽管它可能不会触发错误,因为它没有做太多事情):

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);
    }
}

I have a C++ library that is called by Java through a SWIG-based interface. On the Java side, I build up a structure containing pointers to arrays of other structures, using the default struct interface and carrays.i's %array_class.

Because Java's garbage collector is not aware of the members of the top-level struct, the array is sometimes freed, whereon its finalizer delete[]s its backing memory. I need a way around this, preferably without duplicating the struct in Java, since it's rather large.

A minimal example looks like this (although it probably won't trigger the bug since it doesn't do much):

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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

小苏打饼 2025-01-08 06:45:14

对此有几种可能的解决方案。最简单的方法是包装一个可以创建对象而无需 Java“拥有”内存的函数。这可能看起来像:

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

您实际上可以在创建后修改 swigCMemOwn boolean 。类型映射应该能够将其注入到适当的位置(当将 object 传递给 setitem 时)。例如,您可以编写:

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

这需要在第一次看到 object 类之前,并允许您编写类似以下内容:

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

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


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

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

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

%include "carrays.i" 之前以及通过 %typemap(javacode) 相应的 disown() )对象。 (swigCMemOwn保护,因此 objectArray 无法直接修改它)。


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

或者,另一种常见的解决方法是 保留对 Java 代理对象的引用,通过将其分配给成员变量。在这种情况下,因为您可能有很多对象,但它们本身必须是容器。

There's a couple of possible solutions to this. The simplest is to wrap a function that can create objects without Java "owning" the memory. This might look something like:

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

You can actually modify the swigCMemOwn boolean after creation. A typemap should be able to inject this at the appropriate place (when the object is handed to setitem). For example you could write:

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

This needs to be before the object class is first seen and allows you to write something like:

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

instead of os.setitem(i, o);.


A variation on this theme would be to use a javain typemap to replace the default setitem implementation in the Java proxy such that it calls a function (you supply) on the object to change the ownership, e.g.:

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

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

before the %include "carrays.i" and a corresponding disown() via %typemap(javacode) object. (swigCMemOwn is protected, so the objectArray can't modify that directly).


There are other approaches possible too, which involve setting the ownership also. The ones I've shown are the easiest I think though.

Alternatively, another common work around would be to keep a reference to the Java proxy Object hanging around, by assigning it to a member variable. In this instance since you potentially have lots of objects that would have to be a container itself though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文