Элементы структуры SWIG преждевременно освобождаются сборщиком мусора Java - PullRequest
4 голосов
/ 01 февраля 2012

У меня есть библиотека C ++, которая вызывается Java через интерфейс на основе SWIG. На стороне Java я создаю структуру, содержащую указатели на массивы других структур, используя интерфейс структуры по умолчанию и carrays.i s %array_class.

Поскольку сборщик мусора в Java не знает членов структуры верхнего уровня, массив иногда освобождается, и его финализатор delete[] сохраняет свою резервную память. Мне нужно обойти это, желательно без дублирования структуры в 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);
    }
}

1 Ответ

2 голосов
/ 02 февраля 2012

Есть несколько возможных решений для этого. Самое простое - это обернуть функцию, которая может создавать object s без «владения» памятью 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 карты типов для замены стандартной реализации setitem в прокси Java, так что она вызывает функцию (вы предоставляете) на 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" и соответствующего disown() через %typemap(javacode) object. (swigCMemOwn равно protected, поэтому objectArray не может изменить это напрямую).


Возможны и другие подходы, которые также включают установление права собственности. Я думаю, что те, которые я показал, самые простые.

В качестве альтернативы, другим распространенным обходным решением будет сохранить ссылку на прокси-объект Java, висящий вокруг , назначив его переменной-члену. В этом случае, поскольку у вас потенциально есть много объектов, которые должны быть самим контейнером.

...