2012-05-27 6 views
9

Dobry wieczór!Mycie w programie MakeScreenshot?

W bieżącym projekcie mam raczej niepokojący wyciek pamięci, którego nie mogę podłączyć.

Opuściłem aplikację uruchomioną na noc ze standardowym używaniem, a kiedy obudziłem się 8 godzin później, zużywało ~ 750 MB pamięci, podczas gdy zaczynało się ~ 50 MB. Menedżer zadań systemu Windows nie nadaje się do sprawdzania przecieków, poza tym, że pozwala stwierdzić, że taki istnieje w pierwszej kolejności.

Rozwiązałem już kilka innych wycieków pamięci, z których główny jest związany z TGlowEffect Firemonkeys. Nie jest wykrywany przez ReportLeaksOnShutdown, ale użycie pamięci staje się nadmiernie nadmierne w przypadku dynamicznie modyfikowanego obiektu (np. Zmiany obrotu lub skali).

Śledziłem go do licznika czasu (i wyłączając go całkowicie zatrzymuje wyciek), i jeśli to możliwe, potrzebuję pomocy w jego naprawieniu.

Opis: Ten kod wykorzystuje funkcję Firemonkey MakeScreenshot zapisać wizualny wygląd TPanel (SigPanel) do TMemoryStream. Te dane strumieniowe są następnie przesyłane do zdalnego serwera FTP przy użyciu standardowego kodu (patrz poniżej). Wewnątrz SigPanel istnieją dzieci: 4 TLabel, 1 TRectangle i 6 TImage.

Uwagi: CfId jest globalny łańcuch i jest generowany w oparciu na losowej extended wartości pływaka, który jest następnie zaszyfrowaną wraz z DateTime w formacie yyyymmdd_hhnnsszzz. Generowanie to jest wykonywane po utworzeniu formularza i powtarza się, dopóki nie uzyska poprawnego CfId (tj. Nie zawiera znaków niedozwolonych w nazwach plików systemu Windows). Po uzyskaniu poprawnego CfId, nie uruchamia się wcale (ponieważ nie ma już potrzeby generowania nowego identyfikatora). To pozwala mi prawie całkowicie wyeliminować szansę duplikowania CfId.

Kod w stoperze wygląda następująco;

var 
    i : Integer; 
    SigStream : TMemoryStream; 
begin 
    SigStream := TMemoryStream.Create; 
    SigPanel.MakeScreenshot.SaveToStream(SigStream); 
    SigPanel.MakeScreenshot.Free; 
    if VT2SigUp.Connected then 
    begin 
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False); 
    end else 
    begin 
    VT2SigUp.Connect; 
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False); 
    end; 
    SigStream.Free; 
end; 

z samowyzwalaczem NIE uruchomiony, funkcje kodu całkowicie bez przecieków i ReportMemoryLeaksOnShutdown robi NIE wygenerować wiadomość. Po włączeniu timera i zezwoleniu na "uruchomienie" co najmniej raz, dostaję dużo wycieku, który rośnie, tym więcej razy działa timer. Zgłaszane przecieki są następujące;

Small Block Leaks 

1 - 12 Bytes: Unknown x 1 
13 - 20 Bytes: TList x 5, Unknown x 1 
21 - 28 Bytes: TFont x 2, TGradientPoint x 8, TGradientPoints x 4, Unknown x 4 
29 - 36 Bytes: TObjectList<FMX.Types.TCanvasSaveState> x 1, TBrushBitmap x 4, 
TBrushGrab x 4, TPosition x 24, TGradient x 4, UnicodeString x1 
37 - 44 Bytes: TBrushResource x 4 
53 - 60 Bytes: TBrush x 4 
61 - 68 Bytes: TBitmap x 5 
69 - 76 Bytes: TD2DCanvasSaveState x 1 
205 - 220 Bytes: TCanvasD2D x 1 

Sizes of Medium and Large Block Leaks 
200236 

jako przebiegów czasowych, wartości te są mnożone n razy ( n oznacza liczbę razy czasomierz run). Średnie i duże bloki mają wartość z roku 20023636 (np. Jeśli zegar pracował 3 razy, to jest to 200236, 200236, 200326).

Zainteresowanie, jeśli usunę kod związany z MakeScreenshot, wyciek już nie istnieje, a zużycie pamięci pozostaje na nieco normalnym poziomie. Oprócz zwykłego użycia pamięci, nie ma nic niezwykłego i nie ma żadnych wycieków. Próbowałem już wielu próbek kodu, zarówno z zapisaniem do strumienia i przesłaniem z niego, jak i zapisaniem do strumienia> Plikem, a następnie przesłaniem pliku, ale wydaje się, że wyciek w samej funkcji.Dodałem nawet MakeScreenshot.Free kiedy odkryłem przeciek tutaj, ale po prostu nie mogę go podłączyć, i oczywiście użyłem try..finally w jednym z moich "testów" kodu.

Uruchomiłem nawet kod z GDI + jako typ kanwy i wystąpił tam taki sam wyciek (jedyną zmianą jest to, że D2D przesyła referencje GDI +).

Byłbym bardzo wdzięczny za wszelkie badania lub notatki, które ktoś ma na ten temat, a ponadto rozwiązanie tego problemu.

+1

wierzę, po prostu znaleźć przeciek pamięci w FM (: – ComputerSaysNo

+0

wierzę ustawienie 'ReportMemoryLeaksOnShutdown: = True;' w inicjalizacji twojej aplikacji powinno wystarczyć, aby pokazać, co jest nieszczelne ... –

+0

@DorinDuminica Wierzę, że tak, ale uważam, że znalazłem problem polegający na tym, że 'Wynik' w' FMX.Types.MakeScreenshot' nie jest w rzeczywistości są zwalniane, po prostu wywołują "Result.Canvas.EndScene" i nigdy go nie uwalniają! @Je rryDodge Tak właśnie uciekłem, aby dowiedzieć się, co było nieszczelne, dokładnie tak, jakbym nie był w stanie uzyskać dokładnej listy bez niego :) –

Odpowiedz

13

Nie uwalniasz mapy bitowej tworzonej przez MakeScreenshot.

procedure TForm1.Button1Click(Sender: TObject); 
var 
    ms: TMemoryStream; 
begin 
    ms := TMemoryStream.Create; 
    Panel1.MakeScreenshot.SaveToStream(ms); 
    ms.Free; 
end; 

Powyższy kod nie zawiera odniesienia do utworzonej bitmapy, dlatego nie ma szansy na jej uwolnienie. Zamiast zmienić swój projekt jak poniżej:

procedure TForm1.Button2Click(Sender: TObject); 
var 
    ms: TMemoryStream; 
    bmp: TBitmap; 
begin 
    ms := TMemoryStream.Create; 
    bmp := Panel1.MakeScreenshot; 
    bmp.SaveToStream(ms); 
    ms.Free; 
    bmp.Free; 
end; 


W poniższym kodzie jesteś w rzeczywistości, tworząc dwie bitmapy i zwalnia jedną z nich.

SigPanel.MakeScreenshot.SaveToStream(SigStream); 
    SigPanel.MakeScreenshot.Free; 


W końcu, Twój kod będzie bardziej jak poniżej:

var 
    i : Integer; 
    Bmp: TBitmap; 
    SigStream : TMemoryStream; 
begin 
    SigStream := TMemoryStream.Create; 
    try 
    Bmp := SigPanel.MakeScreenshot; 
    try 
     Bmp.SaveToStream(SigStream); 
     if not VT2SigUp.Connected then 
     VT2SigUp.Connect; 
     VT2SigUp.Put(SigStream, 'Sig_'+CfId+'.png', False); 
    finally 
     Bmp.Free; 
    end; 
    finally 
    SigStream.Free; 
    end; 
end; 
+2

Uzupełniając @Sertac answear, nie zapomnij użyć instrukcji try, aby uniknąć błędów podczas wstawiania i uniknąć wycieku memorystream, ponieważ SigStream.Free nie zostanie wywołany, jeśli podniesiony zostanie jakikolwiek wyjątek. –

+0

Ach, oczywiście, to wszystko. Wydaje się, że jest to strasznie rozwlekły sposób i początkowo wyglądało na to, że wynik nie został zwolniony w 'FMX.Types'. Zakładam, że 'MakeScreenshot' musi zostać wywołany i nadany mu odnośnik (np. Strumień), ale zobaczenie, jak to zrobiłeś, pozwala mi zrozumieć, w jaki sposób funkcja ma być używana poprawnie. Jak już wspomniałem, użyłem 'try..finally' w jednej z moich poprawek kodu, ale wyjąłem go, aby uprościć kod tak bardzo, jak to możliwe, aby rozwiązać problem. –

+2

@Scott - W porządku jest pominięcie obsługi błędów podczas publikowania pytania tutaj lub podczas tworzenia wstępnego projektu. Po prostu nie chciałem tego pominąć, gdy zobaczyłem, że wspomniano o komentarzach. –