![]() |
Erneutes Laden von Bitmaps: Speicherplatz freigeben
Hallo allerseits,
ich arbeite zur Zeit an einem Programm, das über Bildbearbeitungsfunktionen verfügt, aber auch durch einen schnellen Klick zwischen allen Bildern eines Ordners durchswitchen kann, wie die Vor-Zurück-Pfeile in der Windows-Fotogalerie, wenn man mal schnell Bilder gucken will. Ich realisiere dies durch Laden des aktuellen Bildes in eine Klasse, unter anderem mit den folgenden Bitmaps
Delphi-Quellcode:
Diese Bitmaps brauche ich für die Bearbeitungsfunktionen; current stellt die Bitmap mit der aktuellen Version des Bildes dar, ist aber zur Öffnungszeit mit original gleich.
type Tbild = class
original, current: TBitmap; last: array [1..10] of TBitmap; {.....} end; Mein Problem: Beim eben erwähnten "Bilder-Gucken" geht der Speicher sehr schnell fritte, nach ca 20-30 10MP-Bildern ist zumindest an meinem PC Schluss ("Für den angeforderten Befehl steht kein Speicher mehr zur Verfügung"). Natürlich liegt das an den obigen zwölf Versionen meines Bildes. Allerdings ändert sich dadurch auch nichts, dass ich vor dem Laden eines neuen Bildes versuche, den ganzen Speicherplatz wieder freizugeben: Habe nun über freeandnil(bild) hin zu
Delphi-Quellcode:
alles versucht, es hilft nichts.
bild.current.Free;
bild.original.Free; for i := 1 to 10 do bild.last[i].free; bild := TBild.init Wer hat eine Idee, wo mein Fehler sein könnte, bzw. der Speicher nicht freigegeben werden kann? Danke im Voraus! |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Delphi-Quellcode:
gibt ja nur das TBild-Objekt frei und setzt die Variable auf nil.
FreeAndNil(Bild)
Wenn du also keinen Destructor erstellt hast, in welchem du die enthaltenen TImages freigibst, dann bleiben diese Subobjekte natürlich erhalten. Eventuell ![]() ![]() Was hast du denn sonst noch für Codes? Das kann ja nicxht alles sein. |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Gut, aber dann müsste es doch mit dem letzten Codebeispiel klappen? Da "free"e ich ja die Subbilder einzeln.
|
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Es tut sich also auch tatsächlich nichts, wenn ich so alle Bilder in "Bild" einzeln freeandnille:
Delphi-Quellcode:
freeandnil(bild.current); freeandnil(bild.original); for i := 1 to 10 do freeandnil(bild.last[i]); freeandnil(bild); bild := Tbild.init; bild.open(pname); |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Jupp, diese Bilder würdes du damit freigeben,
aber wie erstellst du die Bilder? Womöglich überschreibst du da ständig die Komponenten und vergißt die Alten freizugeben. |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Es wäre ohnehin sauberer, wenn die Klasse TBild die Bitmaps selbst freigibt (spätestens im Destruktor). Dann bräuchte man von außen nämlich nur diese Instanz freizugeben, und alle Bilder wären sauber entsorgt.
|
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Gut, also jetzt mal systematisch.
Wenn ich zu einem anderen Bild "umschalte", geschieht Folgendes:
Delphi-Quellcode:
Dabei ist bild.destroy der von mir definierte Destructor, ja, mit override:
bild.destroy;
bild := Tbild.init; bild.open(pname);
Delphi-Quellcode:
Der Constructor führt Folgendes durch:
destructor Tbild.destroy;
var i: integer; begin current.Free; original.Free; for i := 1 to 10 do last[i].Free end;
Delphi-Quellcode:
Und schließlich hänge ich der Übersichtlichkeit halber auch noch die ganze open-Prozedur an:
constructor TBild.init;
var i: integer; begin current := TBitMap.create; for i := 1 to 10 do last[i] := TBitMap.create; original := TBitMap.create; changed := false; showtext := true; setlength(textarray,0) end;
Delphi-Quellcode:
Ja, es mag nicht besonders speicherschonend sein, direkt bei Open zehnmal das Original ins Array zu laden. Aber dies ist nun ziemlich fest im Programm verwurzelt und eigentlich kann dort auch nicht das Problem sein. Denn nach meiner Schreibweise müssten die ja auch komplett wieder freigegeben werden.
procedure TBild.Open(pname: string);
var jpg: TJPEGImage; i: integer; begin checksave; if lowercase(extractfileext(pname))='.jpg' then begin jpg := TJPEGImage.Create; jpg.loadfromfile(pname); original.assign(jpg); end else if lowercase(extractfileext(pname))='.bmp' then original.loadfromfile(pname); for i := 1 to 10 do last[i].Assign(original); name := pname; current.Assign(original); changed := false; application.title := Programmtitel+' | '+extractfilename(bild.name); with form1 do begin dateiliste(ExtractFilePath(bild.name), form1.datlist.Items); lb_groesse.Caption := inttostr(current.width)+'x'+inttostr(current.Height); lb_resolution.caption := 'Auflösung: 100% - '+inttostr(current.width)+'x'+inttostr(current.Height); form1.Caption := application.Title; lb_name.caption := extractfilename(bild.name) end; screenzoom; display end; Trotzdem muss bei mir pro Bild 1% meines RAM dran glauben. Jemand einen Tipp? |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Das wichtigste zuerst: Niemals Destroy() direkt aufrufen, immer Free() oder gleich FreeAndNil() - die Predigt haben wir hier öfter ;). Das wird in diesem Fall gut gehen, es gibt aber andere, in denen das nicht so geht, von daher besser sofort abgewöhnen.
Dann noch eine Stilsache: Der Konstruktor heisst Create(), nicht Init()! Von "Create" abweichende Namen sollte man nur für statische Methoden nehmen, die Factory-artig arbeiten, und selbst da sollte es immer noch irgendwie "Create" drin vorkommen. Du schlabberst dir bei einem 10MP Bild damit aber auch gleich mal (10*1000000*4)/(1024*1024) ~= 380 MB Speicher voll, das ist ja schon mal was, und nicht die "feine Englische Art". In Open() wird "jpg" nie freigegeben, und ich bin mir fast sicher, dass TJpegImage intern eine dekomprimierte Version vorhält, ggf. noch weitere Metadaten, vor allem aber GDI Resourcen belegt, die schnell mal knapp werden können. Eventuell schafft das schon Abhilfe. Zudem ist es mir schon ab und an passiert, dass mir FastMM Lecks angezeigt hat, wenn ich Arrays nicht mit Finalize() explizit und "ganz" platt gemacht habe, das ggf. also auch noch rein. Ich habe aber eher GDI Resourcen im Verdacht, nicht blos das RAM, auch wenn du das ziemlich mies behandelst ;) Was machen checksave, screenzoom und display? (Auch eher schlechte Namen übrigens, und sie riechen sehr deplatziert.) |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Problem gelöst - das Freigeben vom jpg bei Open war's.
Danke @Medium! Ich hatte ehrlich gesagt angenommen, dass sich die lokalen Variablen schon von selbst erledigen. Und danke für die anderen formalen Tipps, ich bin halt nur Gelegenheitsprogrammierer. checksave, screenzoom und display haben dort allerdings durchaus ihren Sinn ;-) Fürs Protokoll: sie platzieren das Bild vor allem oberflächenfüllend in der Mitte des Programms! |
AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben
Und dann solltest du besser auf globale Variablen verzichten.
Wenn dein TBild das Form1 kennen soll, dann übergib es ihm im Constructor, über ein Property und speichere dieses in einem Feld, oder als Parameter direkt an das Open, anstatt auf Form1 zuzugreifen. Wobei ich alles ab application.title nicht in die Klasse reinmachen würde, da man sich so eine untrennbare Verbindung einbaut. Für sowas gibt es Callbacks/Events, wie z.B. das OnChange eines Edits ... man baut den seinen OnChange-Code ja auch nicht direkt in die TEdit-Klasse ein. > wiederverwendbarer Code PS: Du nutzt
Delphi-Quellcode:
und greifst darin nochmal auf Form1 zu, obwohl du dich schon in dessen Scope/Gültigkeitsbereich befindest?
with Form1 do
Ist nicht schlimm, aber "unschön". (abgesehn davon daß man auf sowas besser nicht direkt zugreifen sollte) Zu der Bezeichnung "Init": Diese ist auch noch syntaktisch falsch, denn dort erstellst und initialisierst du das Objekt, anstatt es nur zu initialisieren. Abgesehn davon, daß sich Create als Constructor etabliert hat und jeder weiß was dieses macht, wenn man nur den Namen ließt ... das ist bei init nicht der Fall. > selbsterklärender Code |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:49 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz