Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

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

Re: Speicherverwaltung von Delphi (Compiler)

  Alt 29. Aug 2005, 11:37
Ich sag lieber auch mal was zu diesem Thema, auch wenn's mittler Weile schon 'n bissl spät ist.

Also erstmal dazu, warum ich hier vermutlich mitreden kann:
Und zwar hatte ich auch vor 'ner "ganzen" Weile mal ein derartiges Problem, dabei wurden in einem Programm abundzu einige tausende Strings (also viele - derzeit nur etwas über 20.000 - nette kleine und auch größere Speicherblöcke) erstellt, diese wurden dann überarbeitet und dabei auch öfters mal in der Größe verändert.

Nun sei ja zu beachten, dass die "meißten" von Programmen erstellten Speicherblöcke statisch sind, also nicht verschoben werden können.
Wenn jetzt öfters Speicherplatz reserviert und freigegeben wird, dann entstehen zwischen diesen Blöcken viele Lücken, welche unter umständen nicht mehr genutzt werden können, weil sie z.B. zu klein sind.
Dazu kommen dann noch die ungenutzen Blockanteile, welche durch die Sektorisierung des speichers entstehen ... denn normaler Weise werden die einzelnen Blöcke ja an bestimmten Grenzen ausgerichtet ... wenn jetzt also eine bestimmte Menge an Speicher bestellt wird, dann reservieren die verschiedenen Speichermanager (z.B. die von Delphi und Windows) ja einen kompletten Block und das Blockende bleibt ungenutzt.

Auch zu beachten sein, dass Delphi's Memory Manager und auch die meißten Anderen sich "größere" Speicherblöcke von Windows holen und diese dann intern in "kleineren" Teilen an die verschiedenen Variablen weitervermieten, aber diese Blöcke können natürlich nur wieder an Windows zurückgegeben, wenn auch der geamte Block wieder frei ist ... sollte also auch nur eine winzige Variable noch darin rumrliegen, bleibt der entsprechende Block beim Programm.
Wenn man sich also massig Speicher reserviert und dafür sorgt, das in jedem Block auch nur ein Byte belegt bleibt, nachdem man den größten Teil wieder freigegeben hat, dann muß das Programm diese Blöcke ja behalten, auch wenn die vielen Einzelbytes mit Sicherheit in nur einen einzigen kleinen Block reinpassen würden.

Nun können sich diese nicht nutzbaren Speicherbereiche sogar noch ansammeln ... sowohl über die Anzahl der reservieren Blöcke und natürlich auch durch die ständigen Reservierungen/Freigaben.
Bei mir zeigte sich dieses Verhalten darin, dass anstatt nur effektiv etwa 20 bis 60 MB der tatsächliche Verbrauch auf über 200 MB anstieg und nach einer Weile dann auch mal auf über 700 MB ausweitete, (irgendwann kamm halt dann das "out of memory")
obwohl die Datenmenge im Speicher eigentlich gleich blieb ... die Daten wurden bei mir ja nur zwischen den Strings verschoben und manchmal wurde sogar einer gelöscht, also müßte der Speiche ja eigentlich kleiner und nicht größer werden.

Zuerst dachte ich ja auch an ein Speicherleck und hab Tage lang nach diesem Leck gesucht ... allerdings konnte ich zu meiner Frustration nichts finden ... bis ich dann letztendlich auf dieses Verhalten stieß.

Natürlich könnte man ja auch TStrings und seine Kinder für die Verwaltung vieler Strings verwenden, da dieses nur einen "großen" Speicherbock verwendet und die Strings hinereinandergereiht darin ablegt.
Allerdings ist TStrings und Co. nicht gerade schnell, wenn es um große Datenmengen geht, da ja unter Umständen ein großer Anteil der Daten darin verschoben werden muß, wenn man z.B. in einem der Einzelstrings auch nur ein einziges Zeichen einfügt/löscht, warum ich ja auch auf Einzelne Strings umgestiegen bin ... der enorme Geschwingigkeitszuwachs von teilweise bis zu 3.000% hatte mich zuerst überzeugt und ich konnte/wollte nicht mehr zu TStrings zurück.


Im Endefekt konnte ich es dann nur über eine selbst programmierte Speicherdefragmentierung lösen, welche sich jetzt in "regelmäßigen" Abständen über den Speicher meiner Anwendung hermacht.

Allerdings gibt es für eine Defragmentierung gewisse Einschränkungen.
  • entweder man verwendet von Anfang an dynamische Speicherblöcke
    z.B. indem man den Delphi-Speichermanager umgeht und per GlobalAlloc mit GMEM_MOVEABLE dynamische Blöcke reserviert,
    denn solange der Block UNLOCKED und ohne Zugriff ist, kann windows diesen Block ja beliebig verschieben (mit GlobalLock bekommt man dann an den aktuellen Pointer auf diesen Block)
  • oder man muß bei der Definierung der relevanten Variablen dafür sorgen, dass man selber eine Defragmentierung vornehmen und die Pointer entsprechend anpassen kann.



Wenn man sich über die Speicherverteilung seiner Anwendung informieren will, um z.B. eine derartiger Defragmentierung besser zu erkennen, kann man sich auch einen Speichermanager wie FastMM einbauen und den zugehörigen "Usage Tracker" verwenden.



Ich hab mal meinen kleinen eigenen "FastXMM Usage Tracker" als Demo angehängt.
Dieser basiert auf dem FastMM 4.26 von Pierre le Riche.
Allerdings hab ich seinen Memory Manager sehr stark abgespeckt und mit zum Schluß mit einigen Teilen meines alten Memory Managers aufgerüstet.
Als die größten sichtbaren Veränderungen seien genannt:
  • die jetzt funktionierende Anzeige der von FastXMM reservierten Blöcke
    (Blockanteile werden aus Performancegründen Prozentual auf den Gesammtblock verteilt)
  • eine für die Speicherverwaltung optimalere Verteilung der SmallBlocks
  • die Unterscheidung von "normalem" Speicher und File-Images
    (z.B. ist das Grüne ganz oben die eigene EXE)
    und wenn man jetzt mit der Maus mal darüber geht, dann sollte (wenn möglich) unten rechts, bei der Adressanzeige, der Modulname auftauchen


Aber bevor jetzt Fragen kommen ... der Speichermanager steckt in meinem UCC drin und seit Kurzem hab'sch davon auch 'ne Single-Unit-Version (ohne UCC).
Diese scheint jetzt auch zu funkionieren, allerdings muß ich die Units noch 'n bissl aufräumen. (beim nächsten Besuch werd'sch diese dann wohl mitbringen)
Na ja, ich FastMM auch erst durch diesen Beitrag entdeckt und das ist ja auch noch nicht so lange her.





So, das hab ich jetzt mal gebraucht ... komm ja in letzter Zeit nicht so oft zum Schreiben ^^
Angehängte Dateien
Dateityp: exe usagetrackerdemo_516.exe (472,5 KB, 21x aufgerufen)
Dateityp: exe mmusagetrackerdemo_947.exe (479,5 KB, 28x aufgerufen)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat