Speicherleck in GDI+ _GDIPlus_BitmapLockBits() ?

  • Hi,
    wie HIER beschrieben, reserviert _GDIPlus_BitmapLockBits() einen "temporären" Puffer, der aber durch ein UnLock nicht mehr freigegeben wird...

    Spoiler anzeigen
    [autoit]

    #include <ScreenCapture.au3>
    _GDIPlus_Startup()

    [/autoit] [autoit][/autoit] [autoit]

    for $i=1 to 10

    sleep(1000)

    [/autoit] [autoit][/autoit] [autoit]

    $hbscreen1=_ScreenCapture_Capture("", 0, 0, -1, -1, False)
    $pbitmap1 = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen1); reserviert auch Speicher, aber der wird von _GDIPlus_ImageDispose($pBitmap1) wieder freigegeben

    [/autoit] [autoit][/autoit] [autoit]

    $BitmapData1 = _GDIPlus_BitmapLockBits($pbitmap1, 0, 0, _GDIPlus_ImageGetWidth($pbitmap1), _GDIPlus_ImageGetHeight($pbitmap1), $GDIP_ILMREAD, $GDIP_PXF24RGB) ; reserviert einen Puffer von B*H*3 Byte
    If @error Then MsgBox(0, "", "Error locking region " & @error)

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_BitmapUnlockBits($pbitmap1, $BitmapData1) ;gibt den Puffer nicht mehr frei :(
    If @error Then MsgBox(0, "", "Error UNlocking region " & @error)

    [/autoit] [autoit][/autoit] [autoit]

    sleep(5000)

    [/autoit] [autoit][/autoit] [autoit]

    _WinAPI_DeleteObject($hbscreen1)
    _GDIPlus_ImageDispose($pBitmap1)

    [/autoit] [autoit][/autoit] [autoit]

    next

    [/autoit]

    Einfach mal das Script starten und im Task-Manager sieht man, daß die Speicherauslastung alle 5 Sekunden um ca 5MB (22´´) steigt.
    Gibt es eine andere Möglichkeit, den Pointer auf die Bitmapdaten zu bekommen?
    ciao
    Andy

  • Wenn man _GDIPlus_BitmapUnlockBits Type "int*" auf "ptr" ändert, dann gehts:

    [autoit]

    DllCall($ghGDIPDll, "int", "GdipBitmapUnlockBits", "hwnd", $hBitmap, "int*", DllStructGetPtr($tBitmapData))

    [/autoit][autoit]

    DllCall($ghGDIPDll, "int", "GdipBitmapUnlockBits", "hwnd", $hBitmap, "ptr", DllStructGetPtr($tBitmapData))

    [/autoit]

    Wie werden eigentlich die beiden DllStructs $tRect und $tData (_GDIPlus_BitmapLockBits) wieder freigegeben?
    Sollte das bei $tData($tBitmapData) eben durch "int*" erreicht werden und muß man nun noch extra $tBitmapData=0 schreiben?

  • Zitat

    Wie werden eigentlich die beiden DllStructs $tRect und $tData (_GDIPlus_BitmapLockBits) wieder freigegeben

    Naja, die Struct hat eine Größe von 40 Byte (nochmal 14 für den Header sind zusammen 54Byte), die lässt man unter den Tisch fallen. Neulich las ich von einem Compiler, welcher bei jedem Byte nicht "ordentlich freigegebenem" Speicher eine Fehlermeldung rauswirft. Bei meiner Art zu Scripten hätte ich da keinen Spass mehr^^

    Um dieses Speicher-Gepfriemel nicht auf die Spitze zu treiben wurde doch m.E. die "Garbage Collection" erfunden, alles was nach einem gewissen Zeitraum nicht zuzuordnen ist, kommt in den Müll, d.h. wird vom System freigegeben. Aber du hast schon Recht, genau so bin ich nämlich auf das Problem gekommen. Bei einigen Bytes ist das noch egal, aber wenn das Script haufenweise Speicher reserviert und nicht mehr freigibt, und dann nach 5 Minuten Windows aufgrund von Speichermangel die Ohren anlegt, dann ist das Geheule über das sog. "WinDOOF" wieder groß....dabei sitzt das größte Problem meistens zwischen den Ohren 40cm vor dem Bildschirm^^
    Jedenfalls vielen Dank für deine Info!

  • Das sollte man vielleicht als Bug melden!?

    Spoiler anzeigen
    [autoit]

    #include <ScreenCapture.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $hScreen, $hBitmap, $aMem, $tData

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()
    $hScreen=_ScreenCapture_Capture("", 0, 0, -1, -1, False)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hScreen)

    [/autoit] [autoit][/autoit] [autoit]

    For $i=1 To 100
    $tData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, _GDIPlus_ImageGetWidth($hBitmap), _GDIPlus_ImageGetHeight($hBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
    _GDIPlus_BitmapUnlockBits($hBitmap, $tData)
    Sleep(50)
    $aMem = MemGetStats()
    ConsoleWrite("original function; step " & $i & "; mem usage: " & $aMem[0] & @LF)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite(@LF)

    [/autoit] [autoit][/autoit] [autoit]

    For $i=1 To 100
    $tData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, _GDIPlus_ImageGetWidth($hBitmap), _GDIPlus_ImageGetHeight($hBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
    _GDIPlus_BitmapUnlockBits_($hBitmap, $tData)
    Sleep(50)
    $aMem = MemGetStats()
    ConsoleWrite("test function; step " & $i & "; mem usage: " & $aMem[0] & @LF)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Func _GDIPlus_BitmapUnlockBits_($hBitmap, $tBitmapData)
    Local $aResult

    [/autoit] [autoit][/autoit] [autoit]

    $aResult = DllCall($ghGDIPDll, "int", "GdipBitmapUnlockBits", "hwnd", $hBitmap, "ptr", DllStructGetPtr($tBitmapData)) ;Type ptr instead of int*
    If @error Then Return SetError(@error, @extended, False)
    Return SetError($aResult[0], 0, $aResult[0] = 0)
    EndFunc ;==>_GDIPlus_BitmapUnlockBits

    [/autoit]
  • eukalyptus: Ich denke schon.

    Wie werden eigentlich die beiden DllStructs $tRect und $tData (_GDIPlus_BitmapLockBits) wieder freigegeben?


    Wenn die Funktion verlassen wird, werden alle lokalen Variablen gelöscht. Der Wert, der mit Return zurückgegeben wird, natürlich nicht. Auch wenn einer Variablen ein neuer Wert zugewiesen wird, wird der alte gelöscht. Das Memory-Management von AutoIt-internen Variablentypen ist also gar nicht schlecht. Nur den von DLLCalls reservierten Speicher muss man von Hand wieder freigeben.

  • Zitat

    Wenn die Funktion verlassen wird, werden alle lokalen Variablen gelöscht

    Das gilt für Variablen, aber nicht für Strukturen! Genau das Problem hatte ich...in einer Func Strukturen definiert, die ich im nachhinein alle "von aussen" wieder löschen musste, dabei ist mir auch diese Lockbit-Geschichte aufgefallen....Die Pointer auf die Struktur kann man ja ByRef nach aussen weitergeben, oder per Return....

  • Das gilt für Variablen, aber nicht für Strukturen! Genau das Problem hatte ich...in einer Func Strukturen definiert, die ich im nachhinein alle "von aussen" wieder löschen musste, dabei ist mir auch diese Lockbit-Geschichte aufgefallen....Die Pointer auf die Struktur kann man ja ByRef nach aussen weitergeben, oder per Return....


    Wenn man die Strktur nicht mit ByRef oder Return zurückgibt, dann wird sie auch wieder gelöscht.

    Spoiler anzeigen
    [autoit]

    Global $FuncStats

    [/autoit] [autoit][/autoit] [autoit]

    $Ptr = _Test1()
    $t2ndStruct = DllStructCreate("wchar[100]" , $Ptr)
    ConsoleWrite("After return (as pointer): " & DllStructGetData($t2ndStruct,1) & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    Func _Test1()
    Local $tStruct = DllStructCreate("wchar[100]")
    DllStructSetData($tStruct,1,"teststring dfs dsfs dfs")
    ConsoleWrite("In Function: " & DllStructGetData($tStruct,1) & @CRLF)
    Return DllStructGetPtr($tStruct)
    EndFunc

    [/autoit]


    Und wird die ganze Struktur per Return oder ByRef zurückgegeben, dann existiert sie natürlich außerhalb der Funktion weiter und wird gelöscht, wenn die Variable einen neuen Wert zugewiesen bekommt und wenn das nicht passiert, dann existiert sie wie jede globale Variable bis zum Skriptende.

    Die Entwickler haben sich bei der Implementierung schon was gedacht ;) Nur Speicher, der über DLLs und DLLCall angefordert wird, kann nicht in die automatische Verwaltung einbezogen werden. DLLStructs sind aber AutoIt-intern und damit in die Speicherverwaltung integriert.