AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia Erneutes Laden von Bitmaps: Speicherplatz freigeben
Thema durchsuchen
Ansicht
Themen-Optionen

Erneutes Laden von Bitmaps: Speicherplatz freigeben

Ein Thema von var-king · begonnen am 27. Sep 2011 · letzter Beitrag vom 28. Sep 2011
Antwort Antwort
Seite 1 von 2  1 2      
var-king

Registriert seit: 8. Jan 2009
10 Beiträge
 
Turbo Delphi für Win32
 
#1

Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 12:50
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:
type Tbild = class
  original, current: TBitmap;
  last: array [1..10] of TBitmap;
  {.....}
end;
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.

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:
  bild.current.Free;
  bild.original.Free;
  for i := 1 to 10 do
    bild.last[i].free;
  bild := TBild.init
alles versucht, es hilft nichts.
Wer hat eine Idee, wo mein Fehler sein könnte, bzw. der Speicher nicht freigegeben werden kann?

Danke im Voraus!
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.016 Beiträge
 
Delphi 12 Athens
 
#2

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 13:40
FreeAndNil(Bild) gibt ja nur das TBild-Objekt frei und setzt die Variable auf nil.
Wenn du also keinen Destructor erstellt hast, in welchem du die enthaltenen TImages freigibst, dann bleiben diese Subobjekte natürlich erhalten.

Eventuell Delphi-Referenz durchsuchenFreeAndNil auch für die Subbilder nutzen, damit du mit Delphi-Referenz durchsuchenAssigned erkennen kannst, ob da jeweils ein TBitmap-Objekt enthalten ist.



Was hast du denn sonst noch für Codes?
Das kann ja nicxht alles sein.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
var-king

Registriert seit: 8. Jan 2009
10 Beiträge
 
Turbo Delphi für Win32
 
#3

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 14:50
Gut, aber dann müsste es doch mit dem letzten Codebeispiel klappen? Da "free"e ich ja die Subbilder einzeln.
  Mit Zitat antworten Zitat
var-king

Registriert seit: 8. Jan 2009
10 Beiträge
 
Turbo Delphi für Win32
 
#4

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 14:56
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);
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.016 Beiträge
 
Delphi 12 Athens
 
#5

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 15:47
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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.617 Beiträge
 
Delphi 12 Athens
 
#6

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 15:51
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.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
var-king

Registriert seit: 8. Jan 2009
10 Beiträge
 
Turbo Delphi für Win32
 
#7

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 22:32
Gut, also jetzt mal systematisch.

Wenn ich zu einem anderen Bild "umschalte", geschieht Folgendes:

Delphi-Quellcode:
  bild.destroy;
  bild := Tbild.init;
  bild.open(pname);
Dabei ist bild.destroy der von mir definierte Destructor, ja, mit override:

Delphi-Quellcode:
destructor Tbild.destroy;
var i: integer;
begin
  current.Free;
  original.Free;
  for i := 1 to 10 do
    last[i].Free
end;
Der Constructor führt Folgendes durch:

Delphi-Quellcode:
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;
Und schließlich hänge ich der Übersichtlichkeit halber auch noch die ganze open-Prozedur an:

Delphi-Quellcode:
procedure TBild.Open(pname: string);
var jpg: TJPEGImage;
    i: integer;
begin
  checksave;
  if lowercase(extractfileext(pname))='.jpgthen
  begin
    jpg := TJPEGImage.Create;
    jpg.loadfromfile(pname);
    original.assign(jpg);
  end else
  if lowercase(extractfileext(pname))='.bmpthen
    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;
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.

Trotzdem muss bei mir pro Bild 1% meines RAM dran glauben.

Jemand einen Tipp?
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.685 Beiträge
 
Delphi 2007 Enterprise
 
#8

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 27. Sep 2011, 23:38
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.)
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)

Geändert von Medium (27. Sep 2011 um 23:42 Uhr)
  Mit Zitat antworten Zitat
var-king

Registriert seit: 8. Jan 2009
10 Beiträge
 
Turbo Delphi für Win32
 
#9

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 28. Sep 2011, 00:00
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!
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.016 Beiträge
 
Delphi 12 Athens
 
#10

AW: Erneutes Laden von Bitmaps: Speicherplatz freigeben

  Alt 28. Sep 2011, 00:16
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 with Form1 do und greifst darin nochmal auf Form1 zu, obwohl du dich schon in dessen Scope/Gültigkeitsbereich befindest?
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
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (28. Sep 2011 um 00:21 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:44 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz