Benutzer mit den meisten Antworten
Memory Leak

Frage
-
Hallo
Ich habe ein Java Programm geschrieben, dass über JNI auf eine, mit VisualStudio2005 geschriebene, native DLL zugreift. Diese native DLL wiederum ruft eine .NET Assembly auf, über die eine Bilddatei eingelesen wird und diese Bilddatei wird in Form eines unsigned char Array an Java zurückgegeben.
Das ganze funktioniert eigentlich auch, allerdings enthält die Anwendung ein Speicherleck, denn wenn ich die Anwendung über meherere Stunden laufen lasse, in denen ständig neue Bilddateien angefordert werden, wird der virtuelle Speicher immer größer, bis die Auslagerungsdatei zu groß ist.
Ich habe anschließend natürlich versucht den dynamisch angeforderten Speicher wieder frei zu geben, allerdings funktioniert das Programm dann überhaupt nicht mehr, sondern es wird eine EXCEPTION_ACCESS_VIOLATION ausgelöst. Meine Frage ist also, wann gebe ich den Speicher einer Funktion, die als Rückgabewert einen Zeiger auf einen dynamisch erzeugten Speicherbereich zurück gibt, wieder frei.
Das ist der native Code:
typedef unsigned char byte; JNIEXPORT jbyteArray JNICALL Java_model_VMLoadSlideData_getSubRegion (JNIEnv * env, jobject obj, jstring s, jint recX, jint recY, jint width, jint height, jint sizeX, jint sizeY) { printf("%s", "unmanaged\n"); jbyteArray result; const char *str; byte *bptr; const jbyte *jb; str = env->GetStringUTFChars(s, NULL); if(str == NULL) { printf("%s", "unmanaged: String == NULL"); return NULL; } bptr = managed_bridge_getSubRegion(str, recX, recY, width, height, sizeX, sizeY); int len = (jint) bridge_function_length( str, recX, recY, width, height, sizeX, sizeY ); result = env->NewByteArray(len); if(result == NULL) return NULL; jb = (jbyte*) bptr; byte o = *bptr; env->SetByteArrayRegion(result, 0, len, jb); if(*bptr == NULL) printf("%s", "uman_bptr == null\n"); env->ReleaseStringUTFChars(s, str); //env->ReleaseByteArrayElements(result, bptr, 0); return result; }
Das ist der C++/CLI Code:
__declspec(dllexport) byte *managed_bridge_getSubRegion(const char *input, int recX, int recY, int width, int height, int sizeX, int sizeY) { byte *erg; IntPtr ptr2; try { Bitmap ^bm; String ^s = gcnew String( input ); VM::VSAccess::VSProcessor ^vsa = gcnew VM::VSAccess::VSProcessor(s); System::Drawing::Rectangle ^rect = gcnew System::Drawing::Rectangle(recX, recY, width, height); Size ^size = gcnew Size(sizeX, sizeY); bm = vsa->GetImagePart(*rect , *size); System::Drawing::Rectangle ^rect2 = gcnew System::Drawing::Rectangle(0, 0, bm->Width, bm->Height); BitmapData ^data = bm->LockBits( *rect2, ImageLockMode::ReadOnly, bm->PixelFormat); int bytes = data->Stride * bm->Height; byte *arr = new byte[ bytes ]; //byte arr[bytes]; try { IntPtr ptr = data->Scan0; erg = (byte*) (void*)ptr; } catch(Exception ^q) { Console::WriteLine("inner managed Exception"); Console::WriteLine(q->ToString()); } for(int i = 0; i < bytes; i++) { arr[i] = erg[i]; } bm->UnlockBits(data); erg = arr; //delete[] arr; } catch(Exception ^e) { Console::WriteLine(e->ToString()); } return erg;
- Verschoben Martin RichterModerator Freitag, 4. Juni 2010 18:38 (aus:Visual C++)
Antworten
-
Du kannst gcnew an dieser Stelle nicht verwenden, denn dazu müsstest Du den Speicher pinnen.
Warum schreibst Du keine zweite Funktion, die den delete ausführt, dann wenn Du es verantworten kannst?
Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de- Als Antwort markiert Martin RichterModerator Mittwoch, 26. August 2009 06:31
Alle Antworten
-
Der Zeiger den Du zurückgibst wird mit new (Native nicht CLI)) allokiert und wird nicht freigegeben. Bzw. wie machst Du das denn.
Da die DLL zwingend mit der MS-CRT DLL arbeitet müsstest Du ach eine entsprecende Funktion in der DLL aufbauen, die den entsprechenden delete macht.
Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de -
Hallo Martin
Danke für die Antwort. Also die beiden Funktionen sind in zwei unterschiedlichen Source Files. Das Source File mit der JNI-Funktion ist einer unmanaged DLL, in der sich auch die Funktion Java_model_VMLoadSlideData_getSubRegion() befindet. Diese ruft die Funktion managed_bridge_getSubRegion() auf, welche in einer managed dll ist.
Den dynamisch erzeugten Speicher aus managed_bridge_getSubRegion() muss irgendwo frei gegeben werden, allerdings bekomme ich, wenn ich dies in managed_bridge_getSubRegion() mache, eine EXCEPTION_ACCESS_VIOLATION, was ja auch logisch ist, da ich mit den Werten ja noch weiter arbeite.
Ich habe schon versucht diese Zeile,
byte *arr = new byte[ bytes ];
durch diese zu ersetzen.
Byte *arr = gcnew Byte[ bytes ];
Denn dann würde ja der Speicher durch den GarbageCollector freigegeben. Allerdings hat das nicht auf anhieb funktioniert und ich müsste dann mit Byte anstatt byte in JNI weiter arbeiten und ich weiss nicht ob das geht.
Ich habe auch schon probiert den bptr Zeiger in der JNI Funktion mit delete frei zu geben, also den Zeiger der von managed_bridge_getSubRegion() zurückgegeben wird, nachdem ich
ausgeführt habe, weil ich dachte dadurch werden die Werte von bptr in einen Java Datetyp kopiert, aber das hat auch nicht funktioniert.env->SetByteArrayRegion(result, 0, len, jb);
Eine andere Lösung könnte sein, dass ich eine Klasse um managed_bridge_getSubRegion() baue, habe ich mir überlegt. Da ich allerdings ein nicht sonderlich erfahrener C++ Programmierer bin, bin ich um jede Anregung sehr dankbar! -
Du kannst gcnew an dieser Stelle nicht verwenden, denn dazu müsstest Du den Speicher pinnen.
Warum schreibst Du keine zweite Funktion, die den delete ausführt, dann wenn Du es verantworten kannst?
Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de- Als Antwort markiert Martin RichterModerator Mittwoch, 26. August 2009 06:31
-
Hallo Martin
Danke nochmal. Ich hab gerade eine solche Funktion geschrieben, nachdem ich den Buffer global gemacht habe und jetzt funktionierts!!
Das Programm läuft jetzt schon fast 90 Minuten, ohne das die Auslagerungsdatei größer wird.
Viele Grüße,
Michael