【问题标题】:SWIG java: releasing memory allocated in c++SWIG java:释放在 C++ 中分配的内存
【发布时间】:2014-01-11 22:47:35
【问题描述】:

我在 Ubuntu 中使用 SWIG 2.0.10 在 Java 中调用 C++ 代码。

我的 C++ 代码是:

//ImgPro.h:
#include <vector>

typedef struct _bin
{
    char* name;
    float value;
} Bin;

typedef struct imgprops
{
    std::vector<Bin> color;
    int width;
    int height;
    char *print;
} ImageProperties;

class ImgPro
{
public:
    ImgPro();

    ImageProperties *processImage(char* imagePath);
};

processImage函数定义为:

ImageProperties* ImgPro::processImage(char *imagePath)
{
    ImageProperties* imgProp = new ImageProperties();
    imgProp->width = 200;
    imgProp->height = 200;

    char* fp = new char(5);
    strcpy(fp, "abc!");
    imgProp->print = fp;

    Bin outputBin1;
    char *name1 = new char(strlen("red")+1);
    strcpy(name1, "red");
    outputBin1.name = name1;
    outputBin1.value = 0.125;

    Bin outputBin2;
    char *name2 = new char(strlen("blue")+1);
    strcpy(name2, "blue");
    outputBin2.name = name1;
    outputBin2.value = 0.27;

    vector<Bin> tempVec;
    tempVec.push_back(outputBin1);
    tempVec.push_back(outputBin2);
    imgProp->color = tempVec;
    return imgProp;

}

所以,为了使用 swig 生成 jni 代码,我使用了以下 swig 文件(注意:vector.i 文件是使用此 example 创建的):

%module CBIR

// to handle char** has String_Array in Java
%include <various.i>
%include "vector.i"

%{
   #include "ImgPro.h"
%}

// to handle char** has String_Array in Java
%apply char **STRING_ARRAY { char ** };

// memory release
%extend imgprops {
       ~imgprops(){
    if($self != NULL)
    {
        // releasing print element
        if($self->print != NULL)
            delete[] $self->print;
        // releasing vector elements
        for(uint x = 0; x < $self->color.size(); x++)
        {
                Bin currentBin = $self->color[x];
                if(currentBin.name != NULL)
                    delete[] currentBin.name;
        }
        // releasing stuct Pointer
        delete $self;
    }
}
}

%include "ImgPro.h"

%template(BinVec) std::vector<Bin>;

这会在 swig_wrap 文件中生成下一个函数:

SWIGINTERN void delete_imgprops(imgprops *self){
    if(self != NULL)
    {
        // releasing print element
        if(self->print != NULL)
            delete[] self->print;
        // releasing vector elements
        for(uint x = 0; x < self->color.size(); x++)
        {
                Bin currentBin = self->color[x];
                if(currentBin.name != NULL)
                    delete[] currentBin.name;
        }
        // releasing stuct Pointer
        delete self;
    }
}

在删除 ImageProperties c++ 函数中调用。

但是,在 Java 中运行以下代码永远不会释放在 C++ 中分配的内存(调用函数 delete_imgprops):

ImgPro imgObject = new ImgPro();
ImageProperties propObject = imgObject.processImage("imagem123-jpg");            

int width = propObject.getWidth();
int height = propObject.getHeight();
String fingerPrint = propObject.getPrint();

propObject.delete();            
imgObject.delete();

所以,在分析了代码流之后,我找到了内存没有释放的原因。 SWIG 生成的 ImageProperties.Java 文件包含删除函数等:

public synchronized void delete() {
   if (swigCPtr != 0) {
     if (swigCMemOwn) {
       swigCMemOwn = false;
       CBIRJNI.delete_ImageProperties(swigCPtr);
     }
     swigCPtr = 0;
   }
}

“CBIRJNI.delete_ImageProperties(swigCPtr);”行永远不会调用,因为 var swigCMemOwn 始终为假。

我明白因为Java端不分配内存所以它也不会释放它,那么我该怎么做才能确保java释放内存而不对swig生成的java文件进行任何修改?

我发现释放内存的解决方案是在 delete() 函数上注释 if(swigCMemOwn) 测试,但我认为这不是最好的方法!

谢谢,塞尔吉奥

【问题讨论】:

    标签: java c++ memory-leaks swig


    【解决方案1】:

    你可以去看看

    %newobject
    

    带有工厂方法的指令(在 swig 2.0 中)。它告诉 swig 一个特定的函数将返回一个新对象,并且 java 代理类也应该负责清理 c++ 内存。生成的代码会将 swigCMemOwn 设置为 true,从而调用 c++ 析构函数。

    如果您自己调用 delete 方法,那很好 - 您只需改变您的编程风格,将 swig'ed 对象视为文件句柄或数据库连接之类的东西。完成后对这些对象调用 close() ,因为您不想等待 java 的 GC 在稍后某个未知时间启动并收集这些昂贵的资源 - 您想手动管理它。

    但你显然也必须记住要遵守良好的代码纪律,以确保在调用 delete() 后不要在任何地方使用 java 对象

    【讨论】:

      【解决方案2】:

      您永远不应该手动拨打delete()。如果您正确实现了 C++ 析构函数(或在任何地方释放了内存),那么一旦 Java 包装器对象被释放,内存就会被释放,然后 SWIG 包装器代码将自动调用适当的方法。在SWIG doc for Java 阅读更多内容。

      【讨论】:

      • 我只是在测试阶段手动调用 delete() 来查看内存是否被释放(因为在这个测试阶段我有一个在调用垃圾收集器之前存在的小程序) .但我的主要疑问是我应该在 swig 文件中(甚至在 c++ 实现中)进行哪些更改,以确保在 delete() 方法上释放内存而不进行任何修改。
      • 文档实际上说:“理想情况下你不需要调用 delete()”。虽然您是正确的,Java 包装器会在垃圾收集时为您调用 delete(),但重要的是要注意这只会在其最后一个引用被释放后发生并且垃圾收集循环运行——这可能永远不会发生!如果您的 C++ 析构函数在您完成对象后立即运行很重要,您将需要手动调用 delete() - 例如当您的 C++ 类使用 RAII 样式的资源管理时,或者当它们拥有 JVM 看不到的大块 C++ 堆时。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-28
      • 1970-01-01
      • 1970-01-01
      • 2018-04-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多