![]() |
SetLength -> Zu wenig Arbeitsspeicher?
Hallo zusammen,
laut Suchfunktion bin ich ja nicht der erste der das Problem hat. Ich habe das Problem, dass wenn meine Anwendung lange läuft (paar Tage), dass irgendwann sporadisch bei folgenden Aufruf
Delphi-Quellcode:
eine Exception entsteht mit "zu wenig Arbeitsspeicher".
var
aLaenge : Integer; begin aLaenge := 77; //Beispiel try SetLength(InputMsg^, 12+aLaenge); except on E: Exception do begin ErrorStr := 'Aufruf von SetLength():'; end; end; end; Es ist aber noch genug Speicher verfügbar laut TaskManager (auch virtueller Speicher). Die Frage ist, wie kann sowas entstehen bzw. wie kann man dies evtl. verbessern / abändern? Ich verwende Delphi 2009 und die Anwendung läuft auf Win2003 Server. Wäre für jeden Tip dankbar. Viele Grüße Alex PS: InputMsg wird als PAnsiString in diese Funktion übergeben... |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Hallo,
Ist die neue Länge des Strings abhängig von seiner alten Länge? d.h. benutzt du Length um die neue Länge zu bestimmen? Und bist du dir sicher, dass du einen gültigen PAnsiString übergibst? |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Delphi kann, bzw. Win32-Anwendungen können standardmäßig nur maximal 2 GB an "virtuellem" Speicher verwalten ... ist der voll, dann knallt's
Ich vermute mal du hast ein Speicherleck, durch dein Rumgepointere oder Anderswo. Scheib mal
Delphi-Quellcode:
in den Constructor deiner Form.
ReportMemoryLeaksOnShutdown := True;
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Hallo,
ich sehe keine Funktion, nur etwas Code. Und der reicht nicht (Laenge nicht definiert). Heiko |
AW: SetLength -> Zu wenig Arbeitsspeicher?
also die Variable ist defintiv gültig.
@hoika, das müsste aLaenge heißen in dem Code (habs geändert). Ist ja auch nur der Inhalt der Funktion (ohne Funktionskopf). Kann man an diesen "maximal 2 GB" etwas machen oder kommt man da nicht rum? Was ist, wenn die Anwendung aber mit der Zeit immer mehr Daten im Bauch halten muss? Gruß Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Ok verstehe ich aber das kann ja wohl keine Lösung sein oder?
Ich sag mal ganz krass, wenn ich viele Stringlisten mit vielen Objekten oder Zeigern habe, dann ist es doch keine Lösung "einfach" bei 2GB den Riegel vor die Tür zu schieben wenn mein Rechner doch 4GB hat... Macht doch kein Sinn oder? Gruß Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
ist der verfügbare Speicher evtl. "nur" fragmentiert? kann man da vielleicht ansetzen und irgendwas machen?
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
Also wenn ich ein Programm im Taskmanager sehen würde, was 2GB Speicher verbraucht, dann wär das aber ganz schnell abgeschossen.... |
AW: SetLength -> Zu wenig Arbeitsspeicher?
PS: Speicherdefragmentierung gibt's auch noch.
du hast z.B. - maximal 2 GB virtuellen Speicher verfügbar - gnügend physischen RAM + Auslagerungsdatei - aber einen voll defragmentierten virtuellen Speicher (es liegen da ja auch noch über DLLs und so'n Zugs rum) Wird ein dyn. Array in seiner größe geändert, dann wird/muß eventuell das Array umkopoert werden, weil es nicht Inplace vergrößert werden kann (liegt schon was Anderes dahinter). Nun brauchst du also: a) irgendwo ein Stückchen freien Speicher, wo die neuen Arraydaten reinpassen b) zwischenzeitlich auch mal bis zu doppelt soviel Speicher für das Array (neuer + alte Daten) [add] ich sage standardmäßig. Und es sind etwa 3,75 GB an virtuellen RAM, den Rest kann man mit 32 Bit nicht verwalten und eingige Bereiche wurden wohl aus Kompatibilitätsgründen (zur 31-Bit-signed-Adressierung) gesperrt. Mein Windows 32 Bit stellt z.B. nur 3,24 zur Verfügung (Integergrenzen und so) und der Rest wird entweder für Treiber werwendet und alles über 4 GB ist eh nicht adressierbar (abgesehn vom LAA / Large-Address-Aware, aber das muß aktiviert sein, das OS muß es unterstützen und deine Anwendung müßte es selbst behandeln) |
AW: SetLength -> Zu wenig Arbeitsspeicher?
hab mir schon sowas gedacht... kannst du vielleicht trotzdem mal wegen dem trick den du angesprochen hast nachgucken?
ich gucke mal, ob man irgendwelche daten wirklich auslagern kann. danke und viele grüße Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
ok, meinte eigentlich auch, dass der Rechner zwar 4GB hat aber ja nur bis max. 3,5 (ca.) adressierbar sind.
Kann mann denn den Speicher irgendwie zur Laufzeit der Anwendung deframentieren? Sicherlich nicht oder? Viele Grüße Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
Zitat:
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Gegen eine Defragmentierung kann man nicht viel machen, außer Speicheroperationen zu optimieren und seltener Speicher neu reservieren, umzureservieren/freizgeben und z.B. Speicherbplätze wiederzuverwenden.
Defragmentieren geht nicht, denn dann müßtest du im gesamten Programm sämtliche Zeiger kennen, welche auf die zu verschiebenden Teile zeigen (welche du nichtmal alle kennst) auch mit anpassen. Hast du denn mal nach Speicherlecks geschaut? (Beitrag #3) Oder installier dir das große FastMM und schau da mal in die MemoryMap (grafische Speichernutzungsübersicht). |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Wozu muss man Speicher überhaupt "defragmentieren"? Ich meine eine Festplatte verliert selber doch auch kaum Speicher wenn sie fragmentiert ist? Da geht es doch nur darum, schnell hintereinander darauf zuzugreifen?
MfG Fabian |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Wenn aber kein zusammenhängender Speicherblock der entsprechenden Größe verfügbar ist, kann man auch kein solchen anfordern
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Speicherblöcke im RAM sind immer zusammenhängend ... Dateien können stückchenweise verteilt sein.
Defragmentieren heißt hier nur, daß zwischen den vielen Speicherblöck viele und eventuell zu kleine Speicherbereiche frei sind, welche man benutzen könnte. Wenn da z.B. nur 20.000 freie Bereiche a maximal 100 Byte überall verteilt rumliegen, sonst alles voll ist und du aber einen Block mit 1000 Byte benötigst, dann paßt der nirgendwo rein,obwohl insgesamt noch genug frei wäre. aber hier sollte man erstmal > nach Speicherlecks ausschau halten und eventuell > die Speichermap anschauen, ob es wirklich zu soeiner Defragmentierung kommt |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Ich werde mir das mal angucken mit den Speicherlecks und Speichermap.
Vielleicht komm ich da ja dahinter... Glaube aber wirklich, dass es wirklich nur an der Fragmentierung liegt da laut TaskManager eben diese 2GB Grenze gar nicht erreicht wird von der Anwendung! Viele Grüße Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Also ich denke wenn im Taskmanager weniger als 2GB angezeigt werden (wie viel sinds denn ca.?), dann sind es auch MIT Datenfragmentierung weniger als 2GB und das Problem liegt nicht an der Menge des Arbeitsspeichers, sondern wahrscheinlich wie ich schon vermutet habe, dass der PAnsiString auf Müll zeigt oder du versehentlich eine zu große Zahl an SetLength übergibst.. Z.B. durch eine uninitialisierte Variable o.ä.
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Hi,
Zu der 2GB/4GB Problematik hier ein vielleicht noch interessanter Artikel: ![]() Ich würde auch eher auf einen Fehler in deiner Programmierung tippen, wie auch schon von Neutral General erwähnt wurde, oder deine Anwendung benötigt tatsächlich soviel, was dann darauf hinausläuft, dass dein Konzept überarbeitet werden sollte ;) Achja, was mir mal aufgefallen war: SetLength(ArrayEinesTyps, 2); rufe ich das einmal auf, so wird der Speicher einmal für zwei ArrayEintraege belegt. Rufe ich das nochmals auf, so wird ungenutzter Speicher dafür neu belegt, der alte aber nicht freigegeben, laut Taskmanager bleibt der RAM genutzt. Lediglich SetLength(ArrayEinesTyps, 0); gibt den Speicher tatsächlich wieder frei. Seit dem verusche ich entweder, die Array-Längen einmal im Programm definieren zu können, oder Objektlisten zu nutzen. Gruß |
AW: SetLength -> Zu wenig Arbeitsspeicher?
@angos:
Der Artikel ist zwar interessant, aber meiner Meinung nach nicht relevant. Es geht ja hier um virtuellen Speicher. Und bei virtuellem Speicher sind haben die im Artikel genannten Beschränkungen keine Bedeutung. |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Moin,
Zitat:
So, also mit folgenden Code bleibt der RAM Verbauch konstant bei 44 MiB (und dabei ist das Array selber schon ![]()
Delphi-Quellcode:
Übrigens wegen der Defragmentierung: Stimmt, naja wer sagt da das man x64 nicht braucht :P Da kann es einen egal sein (sofern die Anwendungd das nutzt).
type
TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private x : array of Integer; public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin SetLength(x, 10000000); end; procedure TForm1.Button2Click(Sender: TObject); begin while True do Button1Click(nil); end; MfG Fabian |
AW: SetLength -> Zu wenig Arbeitsspeicher?
wie hast du das festgestellt, dass der Speicher laut TaskManager nicht freigegeben wird?
Ist dies nur bei einem Array der Fall oder bei jedem Typ? Viele Grüße Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
Delphi-Quellcode:
Das wirst du sehen wie die RAM Auslastung steigt und steigt :P
var
x : TForm; begin while True do x := TForm.Create(nil); end; MfG Fabian PS: Für TForm kannst du ein beliebiges Objekt nehmen, welches nicht automatisiert freigegeben wird. |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Hier mal das reine Defragmentierungsproblem
Delphi-Quellcode:
[]add
program Project8;
{$APPTYPE CONSOLE} uses Types, Windows, SysUtils; function GetMemSize: Integer; var ms: TMemoryStatusEx; begin ms.dwLength := SizeOf(ms); GlobalMemoryStatusEx(ms); Result := ms.ullTotalVirtual - ms.ullAvailVirtual; end; var i: Integer; a: TIntegerDynArray; begin ReportMemoryLeaksOnShutdown := True; try i := 1; while True do begin WriteLn(Format('a: %0.n mem: %0.n', [i / 1, GetMemSize / 1])); SetLength(a, i); i := i * 2; end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; ReadLn; end.
Delphi-Quellcode:
ist noch besser, denn es erzeugt kein "echtes" Speicherloch, da kurz vor Programmende der Owner für's Aufräumen sorgt, :roll:
while True do
TForm1.Create(self); aber wärend des Programmablauf's wirkt sich dieses wie ein Speicherleck aus. |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
MfG Fabian |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Nach der Exception läuft das, wenn man es z.B. in einen Button-OnClick ausführt, in den VCLeigenen Ereignismethodenaufruf-Try-Except-Schutzblock.
Wenn man danach dann die Hauptform schließt, dann nimmt sie alles mit, sobald die VCL Application freigibt und dieses die Hauptform mitnimmt. Alles in OnCreate der Form1/Hauptform und bei der Exception nehmen sich die Objekte selber mit (Exception im Constructor löscht das Objekt wieder) > hier braucht man nichtmal die Schleife, da alles rekursiv |
AW: SetLength -> Zu wenig Arbeitsspeicher?
@Neutral General: Das mein PAnsiString nicht initialisiert ist oder auf Müll zeigt, würde ich jetzt erstmal für mich ausschließen da das besagte Problem nur sporadisch auftritt und nicht so einfach zu greifen ist.
Ich denke schon die ganze Zeit nach, ob es nicht wirklich "einfach" nur an der Fragementierung liegt. Habe jetzt mal folgendes getestet:
Delphi-Quellcode:
Hier ist laut TaskManager ganz eindeutig zu sehen, dass der Speicher anwächst.
var
i: Integer; a: AnsiString; begin try i := 1; while True do begin SetLength(a, i); i := i * 2; end; except //DoAnything... end; end. Jetzt eine kleine Änderung und man sieht im TaskManager den Speicher nicht mehr ansteigen...
Delphi-Quellcode:
Habe mir grade mal ein Programm geschrieben, was mir permanent über MessageQueue Nachrichten an einen Thread schickt mit der Übergabe eines PAnsiString und anschließender Freigabe. Ich teste das ganze jetzt mal nur mit dem SetLength (also ohne vorher das "SetLength(xxx,0)" und gucke mir das mal im TaskManager an.
var
i: Integer; a: AnsiString; begin try i := 1; while True do begin SetLength(a, 0); //<---- NEU SetLength(a, i); i := i * 2; end; except //DoAnything... end; end. Ein Timer schickt alle 1,5 sek ein Telegramm an den Thread und dieser macht mit dem Telegramm erstmal gar nichts. Ist für mich nur zum testen... Habt ihr noch Ideen / Anreize / Vorschläge? Viele Grüße Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Das ist doch wohl trivial, daß der Speicherbedarf ansteigt, wenn Du Gigabyte-weise davon anforderst. Mir ist nur nicht klar, warum es im zweiten Fall nicht zum Anwachsen kommen soll (weil zu schnell geht?) Irgendwann läufts Du doch wohl in beiden Fällen in die Exception, und dann wird doch hoffentlich abgeräumt!? Wenn keine Exception kommt, ist eh nach 32 Durchläufen Schicht wegen 2^32=0 mod 2^32.
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
ich wollte damit auch nicht sagen, dass es mir unklar ist, warum der Speicher steigt. Klar muss er steigen wenn ich jede Menge anfordere...
Aber warum steigt er im zweiten Beispiel nicht? Das ist die spannende Frage? Das irgendwann ein Int-Überlauf entsteht ist auch klar. Aber darum gehts hier nicht :-) Viele Grüße Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Weil du ihn direkt nach dem Reservieren wieder freigibst. Er steigt wahrscheinlich schon, aber er sinkt halt schneller wieder als du gucken kannst...
|
AW: SetLength -> Zu wenig Arbeitsspeicher?
Gut, der Arbeitsspeicher wird nicht linear, sondern mehr chaotisch vergeben, aber im Prinzip sollte es so klar werden
Code:
Wenn Delphi das Array vergrößer/verkleiner muß und das nicht inplace geht, indem hinten dran neuer Speicher angehängt oder entfernt wird, dann muß erstmal neuer Speicher reserviert werden, dann wird der Inhalt des alten Speichers rüberkopiert und danach dieser freigegeben.
. while True do
begin 1 a := nil; // ja, das geht auch ^^ 2 SetLength(a, i); i := i * 2; end; mit zurücksetzen ohne zurücksetzen 2 |* |* 1 | |* 2 |++ |*++ 1 | | ++ 2 |**** | ++**** 1 | | **** 2 |++++++++ | ****++++++++ 1 | | ++++++++ 2 |**************** | ++++++++**************** = |################ |---------------################ Nun ist entsteht hier aber an den alten Speicherstellen eine Lücke, welche kleiner ist, als der nächste zu reservierende Speicher, weswegen dieser da nicht reinpaßt und er somit nicht nutzbar ist. Im Endefekt hat man zwar selber genausoviel Speicher #### genutzt, aber es bleibt auch noch viel Ungenutzer --- übrig. Hier muß man auch noch beachten, daß der Delphispeichermanager Speicher in Blöcken/Gruppen bei Windows anfordert und diesen stückchenweise an unsere Programme abgibt. Diese Blöcke/Gruppen können aber nur an Windows zurückgegeben werden, wenn alle Einzelteil wieder vom Programm freigegeben wurden. Heißt also, von Seiten des Windows ist der ganze Block benutzt, selbst wenn das Programm nur einen Bruchteil davon verwendet. Und jetzt stell dir vor, daß es nicht nur diesen einen(zwei) Teile gibt, sondern Viele und das da schonmal viele Lücken entstehen können. |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Danke Himitsu für deine Erklärung.
Wenn ich das richtig verstehe, ist es für den Speicherverbrauch egal ob ich vorher Nil bzw. das Setlength(a,0) aufrufe aber für die Struktur im Speicher (Lücken) es besser ist, diese Aufrufe vorher zu machen. Gruß Alex |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
Hi, ich hatte dieses Problem öfters mit Record-Arrays kann dir aber auch so nicht sagen, wie das Problem genau zustande gekommen ist. Ich nutze halt seit dem, wenn ich noch mit arrays arbeite zusätzlich einfach das SetLength(x, 0); mittlerweile aus Gewohnheit. Vielleicht liegt es an den Records oder an bestimmten Datentypen im Record?! Gruß angos |
AW: SetLength -> Zu wenig Arbeitsspeicher?
Zitat:
Und wie du siehst angos kommt es bei mir zu keiner Änderung der Speichergröße, weil der Speichermanager wohl intelligent genug ist, und immer den gleichen Speicherblock nutzt. Zitat:
MfG Fabian |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:32 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-2025 by Thomas Breitkreuz