Hilfe! Anscheinend Memory Leak bei RGB24 -> DIB Verarbeitung :( [EDIT]



  • Hallo!

    Als erstes muss ich schonmal zugeben, dass mir C++ noch relativ schwer fällt. Dennoch möchte ich möglichst schnell ein Programm fertigstellen.

    Mein Code sieht wie folgt aus:

    //Some Code...
    	HDIB dDIB;
    	CDC cdcDIB;
    	cdcDIB.CreateCompatibleDC(GetDC());
    
    	for (j=0; j < numOfFrames; j++) {
    		//Some Code...
    
    		hBitmap = CreateBitmapFromPixel(cdcDIB, 256, 384, 24, buffer);
    		dDIB = BitmapToDIB(hBitmap, NULL);
    		if (!ADD_FRAME_FROM_DIB_TO_AVI(dDIB, codec, 60)) break;
    		GlobalFree(dDIB);
    		DeleteDC(cdcDIB);
    		cdcDIB.DeleteDC();
    		GlobalFree(cdcDIB);
    
    		//Some Code...
    	}
    	//Some Code...
    

    Das ganze befindet sich in einer Schleife, die im aktuellen Testfall 285× durchläuft. Bei jedem Durchlauf steigt der Arbeitsspeicher-Verbrauch an. Auch wenn es komplett durchgelaufen ist und ich einen neuen Durchlauf starte, steigt es weiter an. Irgendwo wird also Speicher nicht mehr freigegeben.

    Diese ganzen DC-Sachen blicke ich noch nicht so wirklich. Ich habe die letzten 2 Tage damit verbracht, Beispiele zu wälzen usw. aber hat nichts geholfen. 😞

    - buffer enthält Rohbilddaten im RGB- bzw. BGR-Format.
    - CreateBitmapFromPixel() habe ich irgendwo bei Google gefunden, tut auch brav seinen Job. Konvertiert Rohbilddaten in ein kompatibles Bitmap. (Code)
    - BitmapToDIB ist Teil von dibutil von Microsoft.
    - ADD_FRAME_FROM_DIB_TO_AVI sollte selbsterklärend sein.

    Wenn ich folgende zwei Anweisungen aus der Schleife testweise rausziehe, entsteht der „Speicherschaden“ nicht. Da ich in der Schleife aber auch den buffer neu schreibe (nächster Frame usw.), muss es leider mit in die Schleife. 😉

    hBitmap = CreateBitmapFromPixel(cdcDIB, 256, 384, 24, buffer);
    		dDIB = BitmapToDIB(hBitmap, NULL);
    

    Ich habe bereits stundenlang versucht, den Fehler zu finden, aber habe jetzt das Suchen aufgegeben.

    Nunja, mir ist bewusst, dass dieser Code totaler Schotter ist. Deshalb frage ich ja auch jetzt die Experten. 🙂

    Wäre übrigens super, wenn ihr direkt Code posten könntet, statt jetzt irgendwas groß und breit zu erklären. Wenn das Programm fertig ist, werde ich mich da wohl eh nochmal reinlesen. Leider will ich das Programm halt sehr schnell fertig haben. Und dieses Leck ist der einzige Bug.

    Schonmal vielen, vielen Dank!!

    ------------

    Edit: Es scheint an der SetBitmapBits() aus der Win32 API zu liegen, die in der CreateBitmapFromPixel() Funktion aufgerufen wird. Kommentiere ich das aus, tritt das Problem nicht auf!! Hat einer eine Idee, wie ich das Problem beheben kann? Gibt es Alternativen zu dieser Methode?

    Edit2: Habe memcpy so angepasst, dass es auch damit funktioniert (Variablen umbenannt). Damit tritt der Leak auch auf.

    Edit3: Wenn beide Funktionen (memcpy und SetBitmapBits) nicht ausgeführt werden, tritt der Leak nicht auf!!


  • Mod

    Das ist unsinning:

    DeleteDC(cdcDIB); 
    cdcDIB.DeleteDC();
    

    Der DC wird am Ende durch den Destruktor freigegeben.

    Aber die anderen Funktionen kenne ich nicht.
    Wo wird z.B. die Bitmap von CreateBitmapFromPixel freigegeben?

    Werden in den Unterfunktionen auch alle Objekte, die in den DC selektiert werden auch wieder heruasgenommen, bevor diese zerstört werden?



  • CreateBitmapFromPixel stammt wie gesagt nicht von mir. Ich habe damit aber gestern noch ungefähr 6 Stunden rumexperimentiert.
    Es liegt definitiv wohl an dieser Funktion, bzw. was darin passiert.

    Aus CreateBitmapFromPixel (aktuelle Version):

    //Entweder:
    SetBitmapBits(hBitmap, lBmpSize, pBits);
    //Oder:
    memcpy(pPixels, pBits, lBmpSize);
    

    Sobald ich eine dieser beiden Anweisungen ausführe, tritt der Leak auf.
    Mehrfaches Aufrufen der Funktionen hat keinen zusätzlichen Leak-Effekt.
    Wenn ich keine der beiden aufrufe, sondern hBitmap direkt davor schon zurück gebe, tritt wie gesagt kein Leak auf.
    Und wenn ich beide aufrufe, dann tritt auch nur der eine Leak auf (also kein "mehrfacher Leak").

    Daher denke ich, dass entweder pPixels, pBits oder vielleicht das zurückgegebene hBitmap bei jedem Durchlauf im Speicher bleibt und nicht aufgeräumt wird. Wisst ihr, wie man das behebt (falls es daran liegt)?

    Achja, wenn ich folgende Zeile mehrfach in meinem Programm aufrufe, dann habe ich auch sozusagen einen mehrfachen Leak:

    hBitmap = CreateBitmapFromPixel(hdcDIB, 256, 384, 24, buffer);
    

    Hoffentlich weiß jemand von euch, was da los ist. 😞



  • CreateDIBSection

    Aus http://msdn2.microsoft.com/en-us/library/ms532292(VS.85).aspx

    ...
    As noted above, if hSection is NULL, the system allocates memory for the DIB. The system closes the handle to that memory when you later delete the DIB by calling the DeleteObject function.
    ...

    Du solltest also hBitmap mit DeleteObject freigeben.

    memcpy kommt als Leak Erzeuger nicht in Frage (es werden ja nur Daten von A nach B kopiert).

    SetBitmapBits hab ich mir nicht angeschaut. Ich vermute es wird ebenfalls nur Daten kopieren.

    Außerdem scheinst du nicht immer "sauber" aufzuräumen:

    // alloc resources
    if (!ADD_FRAME_FROM_DIB_TO_AVI(dDIB, codec, 60)) break; 
    // free resources
    

    Falls ich das richtig sehe musst du "free resources" auch machen, wenn du in den "break" Fall gehst.



  • ihoernchen schrieb:

    Du solltest also hBitmap mit DeleteObject freigeben.

    Habe ich jetzt gemacht. Wow, >50% des Leaks ist damit weg! Danke!!

    ihoernchen schrieb:

    Außerdem scheinst du nicht immer "sauber" aufzuräumen:

    // alloc resources
    if (!ADD_FRAME_FROM_DIB_TO_AVI(dDIB, codec, 60)) break; 
    // free resources
    

    Falls ich das richtig sehe musst du "free resources" auch machen, wenn du in den "break" Fall gehst.

    Danke für den Hinweis. Der break-Fall war nur zur Fehlerbehandlung, tritt also in meinem Test nicht auf.

    Jetzt sind's statt ~200 MB nur noch so ~85 MB Leak. Dazu möchte ich anmerken, dass ein unkomprimierter Frame bei mir 288 KB groß ist. 256*384*3 Byte*285 Frames... kommt ziemlich genau hin mit den 85 MB! Irgendwo scheint es also noch einen Leak bei den RGB-Raw-Daten oder der unkomprimierten DIB-Daten zu geben.

    Daher werde ich nochmal einen kompletteren Code von meinem Programm posten:

    CString file_in, file_out;
    //some GUI code
    
    START_AVI(file_out);
    
    FILE *fp = fopen(file_in, "rb");
    fseek(fp, 0, SEEK_END);
    int filesize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    
    int numOfFrames = filesize / 294912;
    
    int i, j;
    char temp;
    HDIB dDIB;
    char buffer[294912] = {0};
    
    for (j=0; j < numOfFrames; j++) {
    	fread(buffer, 294912, 1, fp);
    	for (i=0; i < 294912; i+=3) { //RGB --> BGR
    		temp = buffer[i];
    		buffer[i] = buffer[i+2];
    		buffer[i+2] = temp;
    	}
    
    	HDC hdcDIB = CreateCompatibleDC(NULL);
    	hBitmap = CreateBitmapFromPixel(hdcDIB, 256, 384, 24, buffer);
    	SelectObject(hdcDIB, 0);
    	DeleteDC(hdcDIB);
    	dDIB = BitmapToDIB(hBitmap, NULL);
    	DeleteObject(hBitmap);
    
    	ADD_FRAME_FROM_DIB_TO_AVI(dDIB, codec, 60);
    	DeleteObject(dDIB); //scheint nichts zu bringen
    }
    fclose(fp);
    
    STOP_AVI();
    

    Entdeckt jemand einen Fehler, der den restlichen Leak verursachen könnte? 🙂

    Nachtrag: Wenn ich folgende Zeile 10× vervielfältige, ist der Leak auch größer. Es wird also irgendwie damit zusammen hängen.

    dDIB = BitmapToDIB(hBitmap, NULL);
    

    BitmapToDIB ist eine Funktion von Microsoft aus der "dibutil.c" (Source file for Device-Independent Bitmap (DIB) API.) (Code hier)



  • Vermutung:
    dDIB mit GlobalFree freigeben.

    In Zeile 70 (letzter Code) wird GlobalAlloc benutzt um Speicher für dDIB (bzw. hDIB) zu reservieren.
    (http://msdn2.microsoft.com/en-us/library/aa366574(VS.85).aspx)

    Mit den Funktionen kenn ich mich aber nich aus.



  • ihoernchen schrieb:

    Vermutung:
    dDIB mit GlobalFree freigeben.

    Genial! 😮
    Das hat den 80 MB Leak gefixt!!

    Vielen, vielen Dank an alle, die geholfen haben! 🙂 🙂 😃


Anmelden zum Antworten