2014-01-11 80 views
2

我在Ubuntu中使用SWIG 2.0.10來調用Java中的C++代碼。SWIG 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; 

}

因此,使用大口以產生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下面的代碼永遠不會釋放內存(調用該函數delete_imgprops)在C++中進行分配:

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

因此,分析代碼流之後,我找到了原因爲什麼內存ISN」 t發佈。通過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總是爲false。

據我所知,因爲Java方面沒有分配內存,所以它也沒有釋放它,所以我能做些什麼來確保java釋放內存,而不對swig生成的java文件進行任何修改?

我發現釋放內存的解決方案是對delete()函數的if(swigCMemOwn)測試發表評論,但我不認爲這是做這件事的最好方法!

感謝,塞爾吉奧

回答

0

你不應該手動調用delete()。如果您正確實現了C++析構函數(或釋放內存的任何地方),只要釋放Java包裝對象,內存就會被釋放,然後SWIG包裝代碼將自動調用適當的方法。閱讀更多信息SWIG doc for Java

+0

我打電話只是在測試階段手動刪除(),看看內存被釋放或沒有(因爲在這個測試階段,我有一個小程序存在於垃圾收集器的調用之前)。 但我主要懷疑我應該在swig文件(甚至在C++實現)中更改哪些內容,以確保內存在delete()方法上發佈而不做任何修改。 – Sergio

+0

該文檔實際上說:「*理想*你不需要調用delete()」。儘管你的Java包裝在垃圾收集時會爲你調用delete(),但是要注意,這隻會在最後一個引用被釋放後纔會發生*和*垃圾收集循環運行 - 這可能永遠不會發生!如果在完成對象後立即運行C++析構函數非常重要,則需要手動調用delete()。當你的C++類使用RAII風格的資源管理,或者當他們擁有JVM看不到的大塊C++堆時。 – jcl

2

你可以檢查出

%newobject 

指令(在痛飲2.0)與工廠方法。它告訴swig一個特定的函數將返回一個新的對象,並且java代理類也應該負責清理C++內存。生成的代碼會將swigCMemOwn設置爲true,從而導致調用C++析構函數。

如果你自己調用delete方法,那很好 - 你只需要改變你的編程風格,把swig的對象看作是文件句柄或db連接。當你完成這些對象時,你可以調用close()方法,因爲你不想等待java的GC在某個未知點後面進入並收集這個昂貴的資源 - 你想手動管理它。

但你也顯然必須記住調用delete()後的任何地方行使好碼的紀律,以確保您不使用Java對象