Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Objektliste sprengt RAM (32-bit) (https://www.delphipraxis.net/189714-objektliste-sprengt-ram-32-bit.html)

Jens01 12. Jul 2016 15:14

Objektliste sprengt RAM (32-bit)
 
Hallo...
ich habe Probleme mit einer TObjectlist, in der ich Ergebniswerte mit Selektoren abspeichere. In der Liste können schon mal mehrere zehntausend Werte auflaufen. Ab einer bestimmten Menge gibt es ein "Out of Memory"-Fehler und das Programm stürzt ab.
Was für mich aber unverständlich ist, wenn ich dieselben Daten in ein Stream packe und auf der Festplatte abspeichere, sind da nur wenige Kilobyte zu finden. Wieso bläht sich das so im RAM auf?
Gibt es Möglichkeiten, das zu reduzieren?

Meine prinzipielle Programmstruktur:
Delphi-Quellcode:
  TWert = class
  private
    FWert    : Double;
    FSelektor1: Integer;
    FSelektor2: string;
  public
    constructor Create(AWert: Double; ASelektor1: Integer; ASelektor2: string);
    property Wert: Double read FWert write FWert;
    property Selektor1: Integer read FSelektor1 write FSelektor1;
    property Selektor2: string read FSelektor2 write FSelektor2;
  end;

  TWerteliste = class(TObjectList<TWert>)
  private
  public
    procedure Add(AWert: Double; ASelektor1: Integer; ASelektor2: string);
    function FindWert(ASelektor1: Integer; ASelektor2: string): Double;
  end;
P.S.:
Um Fragen vorzubeugen: Ich nutze keine Datenbank, weil für mein Projekt schon die schnelleren Memorydatenbanken zu langsam sind.

Daniel 12. Jul 2016 15:17

AW: Objektliste sprengt RAM (32-bit)
 
hm.
Der Speicherverbrauch hängt auch ein wenig von der Länge Deiner Strings ab, doch wenn die nicht "zu lang" sind, sollten hunderttausende dieser Einträge kein Problem sein.
Kannst Du das Problem auf ein Minimal-Beispiel mit Testdaten reduzieren?

Zacherl 12. Jul 2016 15:25

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Zitat von Jens01 (Beitrag 1342491)
wenn ich dieselben Daten in ein Stream packe und auf der Festplatte abspeichere, sind da nur wenige Kilobyte zu finden

Das liegt daran, dass die Strings nur als "pointer sized" (unter 32 Bit 4-Byte) Referenzen im Stream gespeichert werden, wenn du das komplette Objekt da reinschreibst.

Jens01 12. Jul 2016 15:31

AW: Objektliste sprengt RAM (32-bit)
 
Also die Strings haben eine ungefähre Länge von 10 bis 30 Zeichen. Allerdings sind da ab-und-zu auch griechische Buchstaben drin.
Ich habe auch schon andere Typen versucht, also Shortstring, ShortInt usw. Hat alles nichts gebracht.
Zitat:

Kannst Du das Problem auf ein Minimal-Beispiel mit Testdaten reduzieren?
Wie meinst Du das? Soll ich das posten?

milos 12. Jul 2016 15:32

AW: Objektliste sprengt RAM (32-bit)
 
Hey,

sicher das es keine (heftigen) Leaks gibt?

Freundliche Grüsse

Uwe Raabe 12. Jul 2016 15:37

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Zitat von Jens01 (Beitrag 1342495)
Also die Strings haben eine ungefähre Länge von 10 bis 30 Zeichen.

...

Wie meinst Du das? Soll ich das posten?

Ja, schon. Deine Klasse hat demnach eine Nutzgröße von ca. 60 Byte. Da sollten selbst Millionen Instanzen keine allzu großen Probleme bereiten. Daher ist das Problem offenbar an anderer Stelle zu suchen. An der Klassendefinition und der von dir avisierten Anzahl liegt es vermutlich nicht.

Jens01 12. Jul 2016 15:39

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Das liegt daran, dass die Strings nur als "pointer sized" (unter 32 Bit 4-Byte) Referenzen im Stream gespeichert werden, wenn du das komplette Objekt da reinschreibst.
Ich hoffe, dass ich Dich richtig verstehe, aber ich baue einen Stream mit den einzelnen Werten und Sektoren zusammen. Ich werfe nicht das Objekt so in den Stream. Es läßt sich auch alles sauber vom Stream in die Liste zurückschreiben.

mkinzler 12. Jul 2016 15:43

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Ich werfe nicht das Objekt so in den Stream. Es läßt sich auch alles sauber vom Stream in die Liste zurückschreiben.
Aber auch in diesem Fall befinden sich nicht alle Strings auf einmal im Speicher.

Jens01 12. Jul 2016 15:45

AW: Objektliste sprengt RAM (32-bit)
 
Delphi-Quellcode:
    FName           : string;
    FHerkunft       : string;
    FLF             : Integer;
    FIndx           : Integer;
    FValue          : TAnyValue;
    FKategorie      : TKategorie;
    FKombiName      : string;
    FKombiGruppe    : TKombiGruppe;
    FKombiRechenIndex: Integer;
So das ist meine reale Liste. (Das oben war nur das Prinzip)
Was mir gerade ins Auge springt, ist dies TAnyValue, was ich nutze.
Kann das das Problem sein? Ich hoffe TAnyValue ist bekannt.

P.S.: TKategorie, TKombiGruppe sind nur so kleine Typen : TKategorie = (eins, zwei, drei ,..);

milos 12. Jul 2016 15:50

AW: Objektliste sprengt RAM (32-bit)
 
Hi,

wie schon erwähnt... Sicher das es keine Memory Leaks sind? Glaube nicht dass das von den ganzen strings kommt...
schau doch mal mit FastMM oder Delphi-Referenz durchsuchenReportMemoryLeaksOnShutdown ( := true; ) ob es da welche gibt.

Falls ja musst du halt ein wenig aufräumen :P

Freundliche Grüsse

Jens01 12. Jul 2016 15:53

AW: Objektliste sprengt RAM (32-bit)
 
Hatte ich vergessen zu erwähnen: Es läuft FastMM.

Und es sind ganz sicher keine Leaks, das prüfe ich ständig.

Jens01 12. Jul 2016 18:12

AW: Objektliste sprengt RAM (32-bit)
 
Danke für die Einschätzungen.

Die kreisen die Suche schon etwas ein. Ich werde erst einmal ein Testprojekt bauen und mich dann zurückmelden.

Sir Rufo 12. Jul 2016 18:16

AW: Objektliste sprengt RAM (32-bit)
 
Also ohne annähernd konkrete Werte kann man so ca. Angaben überhaupt nicht bewerten.

mehrere zehntausend Objekte belegen nur ein paar KiloByte im Stream

Jedes Objekt müsste im Stream so mindestens um die 40 Byte belegen (gerechnet ohne Strings). Bei mehreren zehntausend nehme ich mal 30.000 an und komme auf 1.320.000 Byte was so 1,3 MegaByte entspricht (und das als absolutes Minimum).

Somit passen hier auch die Aussagen insgesamt logisch nicht zueinander.

Aktuell kann es also auch nur unlogische Mutmassungen geben und da passt wohl am besten 42

himitsu 13. Jul 2016 09:47

AW: Objektliste sprengt RAM (32-bit)
 
Hab ich das überlesen, oder wurde nicht gesagt wo es knallt?

* Erstellen eines neuen Datenobjekts
* Laden der Daten, die in das neue Objekt rein sollen
* Befüllen des Datenobjekts
* Hinzufügen des Objekts zur Liste

Beim Hinzufügen muß eventuell die komplette Liste kopiert werden, wenn sich der Speicher nicht inplace vergrößern lässt,
aber für "mehrere 10000 Objekte" sollten dafür keine 150 KB an freiem zusammenhängendem Speicher nötig sein.

Festplatte ... Text als ANSI und im RAM als Unicode = doppelt so viel, plus je paar Byte für die Verwaltung/Alignment.

FastMM ... seit Delphi 2006? ist das schon eingebaut (in einer "leicht" abgespeckten Version)
Und wenn du aber eine eigene "externe" Version einbindest, dann hat ReportMemoryLeaksOnShutdown keine Wirkung, da es nur den internen FastMM steuert.
Der externe FastMM wird über seine Options.inc bedient.

Stevie 13. Jul 2016 14:51

AW: Objektliste sprengt RAM (32-bit)
 
Callstack der Exception?

Out of memory exceptions kommen nicht nur, wenn wirklich kein Speicher mehr da ist.

Jens01 13. Jul 2016 14:58

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Out of memory exceptions kommen nicht nur, wenn wirklich kein Speicher mehr da ist.
Ich habe ein "System-Explorer" mitlaufen. Da kann ich eigentlich gut verfolgen, wie die Rambelegung hochläuft, wenn die Liste gefüllt wird. Ich habe das Problem zurvor auch schon eingekreist, zB indem ich Teile der Liste per Stream auf der Festplatte ausgelagert habe. Dann funktionierte es ohne Fehler. (Die Liste wird in Schüben gefüllt.)

freimatz 15. Jul 2016 09:10

AW: Objektliste sprengt RAM (32-bit)
 
Ist das Problem noch offen?
Genauer als der ProcessMonitor dürfte es sein, den Speicherverbrauch direkt in deinem Code zu messe. Wir haben da:
Delphi-Quellcode:
function GetApplicationMemory(): Int64;
var
  memoryState: TMemoryManagerState;
  blockState : TSmallBlockTypeState;
begin
  GetMemoryManagerState(memoryState);
  with memoryState do begin
    Result := TotalAllocatedMediumBlockSize + TotalAllocatedLargeBlockSize;
    for blockState in SmallBlockTypeStates do
      Inc(Result, blockState.AllocatedBlockCount * blockState.UseableBlockSize);
  end;
end;

Jens01 15. Jul 2016 14:07

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Ist das Problem noch offen?
Ja, ich muss aber erst noch ein Testprogramm erstellen. Das kann noch ein paar Tage dauern.
Dein GetApplicationMemory kann ich aber sehr gut gebrauchen. Sowas habe ich nämlich dafür noch gesucht. Danke!

dummzeuch 15. Jul 2016 20:14

AW: Objektliste sprengt RAM (32-bit)
 
Ein uebliches Problem ist das Vergroessern einer Liste: Sie wird immer um 25% der bisherigen Groesse vergroessert:

Delphi-Quellcode:
procedure TList.Grow;
var
  Delta: Integer;
begin
  if FCapacity > 64 then
    Delta := FCapacity div 4
  else
    if FCapacity > 8 then
      Delta := 16
    else
      Delta := 4;
  SetCapacity(FCapacity + Delta);
end;
Und in SetCapacity wird dann ReallocMem aufgerufen, was ggf. Speicher fuer eine neue Liste anfordert und dann die alten Eintraege kopiert. Das kann dann bei einer grossen Liste schnell zu Problemen fuehren.

Ach ja: MaxListSize ist MaxInt div 16

also ca. 2.000.000.000 / 16 = ca. 125.000.000

Nicht zu vergessen: Auch die Objekte selbst benoetigen Speicher und sorgen dafuer, dass er fragmentiert.

twm

dummzeuch 15. Jul 2016 20:19

AW: Objektliste sprengt RAM (32-bit)
 
Ach ja, noch was: Wenn es bei den Strings viele Duplikate gibt, lohnt es sich evtl. diese zusammenzufuegen:

https://en.wikipedia.org/wiki/String_interning

https://sourceforge.net/p/dzlib/code...StringPool.pas

Das kann den Speicherverbrauch drastisch reduzieren.

ShortString bringt uebrigens keinen Vorteil gegenueber den standard Strings, sie verbrauchen immer 256 Bytes.

twm

Jens01 15. Jul 2016 22:47

AW: Objektliste sprengt RAM (32-bit)
 
Das mit dem Grow is es nicht. Ich hatte schon versucht das runter zu setzen.
Zitat:

Ach ja, noch was: Wenn es bei den Strings viele Duplikate gibt, lohnt es sich evtl. diese zusammenzufuegen:
Ich speichere ja eigentlich keine Strings. Strings gehören zu den Selektoren.
Ich speichere Werte mit TAnyValue eine Art TValue. Das Ding habe ich gerade im Verdacht.

Jens01 15. Jul 2016 22:52

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Nicht zu vergessen: Auch die Objekte selbst benoetigen Speicher und sorgen dafuer, dass er fragmentiert.
Ja genau. Deswegen habe ich eigentlich gefragt, ob das an dem Objekt liegen kann. Kann so eine Fragmentierung so viel den Speicher aufblähen, wenn die Daten selbst eigentlich nur eine sehr kleine Größe haben?

Jens01 18. Jul 2016 16:53

AW: Objektliste sprengt RAM (32-bit)
 
Soo..
Ich habe das jetzt noch mal getestet. Es scheint wirklich an den Strings zu liegen. 3 Stück habe ich davon, kann aber nur ein String durch einen Integer ersetzen. Dies TAnyValue ist unschuldig!

Das Objekt durch ein Record zu ersetzen, bringt ca 20% an Speichereinsparung. Beim Durchsuchen der Liste wird dann aber mehr als das Doppelte an Zeit benötigt.

Ich glaube, ich muss meine Architektur noch mal überdenken....
Oder auf 64-Bit umstellen und Ram kaufen.
Wahrscheinlich beides.

stahli 18. Jul 2016 17:38

AW: Objektliste sprengt RAM (32-bit)
 
Jens, schau mal, ob Dir der Thread etwas hilft: http://www.delphipraxis.net/185886-m...inimieren.html

Ansonsten am besten mal ein Testprojekt mit Dummydaten erstellen...

Jens01 18. Jul 2016 17:55

AW: Objektliste sprengt RAM (32-bit)
 
Danke, Stahli
Ein Dictionary könnte ich noch mal probieren, das könnte Speicher und Performance bringen. Mal sehen...

Zitat:

Ansonsten am besten mal ein Testprojekt mit Dummydaten erstellen...
Habe ich ja gemacht.

stahli 18. Jul 2016 18:14

AW: Objektliste sprengt RAM (32-bit)
 
Zitat:

Zitat von Jens01 (Beitrag 1342928)
Danke, Stahli
Ein Dictionary könnte ich noch mal probieren, das könnte Speicher und Performance bringen. Mal sehen...

Zitat:

Ansonsten am besten mal ein Testprojekt mit Dummydaten erstellen...
Habe ich ja gemacht.

... erstellen und anhängen ;-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:33 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