![]() |
Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Moin !
Wenn ich hiermit versuche die Speicherlast meiner Anwendung auszugeben ...
Delphi-Quellcode:
Dann kriege ich als Wert ~100780.
procedure TForm1.Timer1Timer(Sender: TObject);
var State: TMemoryManagerState; Size, i : Cardinal; begin GetMemoryManagerState(State); Size := 0; for i := 0 to High(State.SmallBlockTypeStates) do Inc(Size, State.SmallBlockTypeStates[i].AllocatedBlockCount * State.SmallBlockTypeStates[i].UseableBlockSize); Inc(Size, State.TotalAllocatedMediumBlockSize + State.TotalAllocatedLargeBlockSize); Form1.Caption := 'Item Assign Test - ' + IntToStr(Size) + ' Memusage'; end; Nur was sagt mir dieser Wert? 100kb Speicher den meine Anwendung nutzt?! Der Taskmanager sagt nämlich was von 3,8MB ... |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Versuch es mal so:
Delphi-Quellcode:
Dem System ist es schnurzpiepe, was du an Speicher tatsächlich belegst.
function GetMemoryReserved : Cardinal;
var State : TMemoryManagerState; idx : Integer; begin Result := 0; GetMemoryManagerState( State ); for idx := low( State.SmallBlockTypeStates ) to high( State.SmallBlockTypeStates ) do Result := Result + State.SmallBlockTypeStates[ idx ].ReservedAddressSpace; Result := Result + State.ReservedMediumBlockAddressSpace + State.ReservedLargeBlockAddressSpace; end; Es zählt was du reservierst (auf welcher Liege liegt dein Handtuch), denn das ist für das System auch nicht mehr nutzbar und gilt somit als belegt/benutzt. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Wenn ich diese Leiche nochmal exhumieren darf: Wenn ich mit diesem Code messe, erhalte ich einen Wert von 5.000.000, während der Taskmanager mir 500.000.000 anzeigt. Was läuft da schief?
Hintergrund ist, dass ich eine variable Anzahl von Bitmaps (aus JPG) in den Speicher vorlade. Sind die Bitmaps sehr groß, gibt es eine EOutOfMemory. Ich würde das gern vorher wissen und vermeiden. Laut Taskmanager-Anzeigen ist bei ca. 1,8 GB Schluss. Wie messe ich das jetzt, was wäre ein Schwellenwert, der nicht überschritten werden sollte (den theoretischen Wert von 2 GB bei einer 32 Bit-Anwendung kenne ich natürlich)? |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Nur zum Test!
Setze mal folgendes in deine DPR-Datei relativ an den Anfang und mache deinen Test mit den Bildern noch einmal:
Delphi-Quellcode:
Damit solltest von von 2 GB auf etwa 3 GB maximalen Verbrauch kommen können.
//
{$SETPEFLAGS IMAGE_FILE_LARGE_ADDRESS_AWARE} // |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Das führt leider nicht weiter. Selbst wenn es funktionierte, käme die Speichererschöpfung halt nur etwas später. Es erklärt auch nicht, warum Sir Rufos Code nicht zu funktionieren scheint.
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Guck mal hier:
Delphi-Quellcode:
Das zeigt den Arbeitssatz an.
procedure TForm1.Button1Click(Sender: TObject);
function GetCurrentProcessMemorySize: Int64; var nWndHandle, nProcID, nTmpHandle: HWND; pPMC: PPROCESS_MEMORY_COUNTERS; pPMCSize: Cardinal; begin nWndHandle := Application.Handle; if nWndHandle = 0 then begin Result := 0; Exit; end; pPMCSize := SizeOf(PROCESS_MEMORY_COUNTERS); GetMem(pPMC, pPMCSize); pPMC^.cb := pPMCSize; GetWindowThreadProcessId(nWndHandle, @nProcID); nTmpHandle := OpenProcess(PROCESS_ALL_ACCESS, False, nProcID); if (GetProcessMemoryInfo(nTmpHandle, pPMC, pPMCSize)) then Result := pPMC^.WorkingSetSize else Result := 0; FreeMem(pPMC); CloseHandle(nTmpHandle); end; begin ShowMessage(IntToStr(GetCurrentProcessMemorySize))); end; Quelle: ![]() |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Wirklich vielen Dank für deine Mühe, Jim, aber das zeigt - wenn ich mich nicht irre - nur den Speicherverbrauch des laufenden Prozesses an. EOutOfMemory kommt aber bei der Gesamtgrenze für das ganze Programm.
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Ich dachte du suchst eine Funktion die den Speicherverbrauch anzeigt? :pale:
Das mit dem OutOfMemory... wieso fängst du den nicht ab und wenn er auftritt, brichst du die aktuelle Funktion ab?
Delphi-Quellcode:
Ich weiß nicht, ob man das so macht, aber was anderes fällt mir nicht ein.
for i := 0 to 100 do
begin try tueirgendetwas; except on E: EOutOfMemory // <=========== begin // <=========== Break; // <=========== end; // <=========== end; end; |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
![]() ![]() ![]()
Delphi-Quellcode:
an
THandle(-1)
![]() Zitat:
Dann kommt noch hinzu, dass große zusammenhängende Speicherbereiche schwieriger zu finden sind, als kleine Blöcke. Je nach Fragmentierung des Heaps kann es dann mal funktionieren und ein anderes Mal nicht. Zitat:
Delphi-Quellcode:
abfangen ist okay, aber meiner Meinung nach nicht optimal, da du dies dann bei so wirklich jedem
EOutOfMemory
Delphi-Quellcode:
,
Create
Delphi-Quellcode:
,
GetMem
Delphi-Quellcode:
, etc. machen müsstest. Natürlich auch bei allen Funktionen, die intern irgendwie Speicher auf dem Heap reservieren.
SetLength
Du solltest mal schauen, ob es wirklich notwendig ist ALLE Bitmaps permanent im Speicher zu halten. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Bei mir persönlich waren Speicherprobleme ausnahmslos immer auf schlechten Code zurückzuführen. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Nein, nein, das hat schon seine Richtigkeit. Es geht um ein Bildanzeigeprogramm. Da biete ich eine Möglichkeit, eine einstellbare Anzahl von Bildern im Speicher vorzuladen, zum Beispiel 5 vor und 2 zurück. Man kann dann auch bei langsamer Verbindung sehr schnell innerhalb dieser 7 Bilder hin- und herspringen, weil sie nicht jedesmal geladen und dekodiert werden müssen. 7 Bitmaps sind schon eine Menge Stoff, das kann locker an 500 MB herangehen. Ein EOutOfMemory gibt es, wenn a) die Anzahl zu hoch eingestellt wird, b) die Bitmaps sehr groß sind. Wenn da ein Panoramabild dabei ist, dann kann es schon bei einer Bitmap knallen.
Das Abfangen von EOutOfMemory funktioniert leider nicht immer, ich vermute, weil da Funktionen aus einer DLL aufgerufen werden, und deren Fehlermeldungen nicht unterdrückt werden können. Hab ich aber noch nicht ganz exakt untersucht, weil ich es lieber gesehen hätte, zum Beispiel bei 1,5 GB das Laden abbrechen zu können bzw. gar nicht erst zu versuchen. Das sollte doch eigentlich mit GetMemoryManagerState möglich sein. Daneben verstehe ich auch nicht: Wie kann die Berechnung um den Faktor 100 daneben liegen? |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Da fällt mir spontan ein, wie es Photoshop doch macht. Da werden die Bilder (Tabs) auf die Festplatte ausgelagert). Wäre das eine Möglichkeit für dich von der Schnelligkeit her? |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Oder, wenn das Auslagern auf die Festplatte wirklich zu langsam sein sollte, einfach die Größe der Bitmaps ermitteln und nach oben hin deckeln?
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Auf keinen Fall. Die Bilder können ja auch auf einem unglaublich langsamen Netzlaufwerk liegen (ich spreche da aus eigener Erfahrung). Außerdem funktioniert das mit dem Speicher ganz ausgezeichnet. Es handelt sich ja nur um ein Grenz-Problem.
Deckeln: Ja, aber das hängt ja vom Speicher ab... Obwohl, könnte ich als Notnagel natürlich auch machen... Aber Gottsakra, warum funktioniert denn die Speichermessung nicht?! |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Wie wäre es denn hiermit:
Delphi-Quellcode:
Du zeigst ja immer nur ein Bild gleichzeitig an, so wie ich das verstanden habe. Dieses Bild lädst du von seinem originalen Speicherort, decodierst es ggfls., so dass du die Bilddaten als
type
TMemoryFileStream = class(THandleStream) public constructor Create; destructor Destroy; override; end; { TMemoryFileStream } constructor TMemoryFileStream .Create; begin inherited Create(CreateFile(PChar(TPath.GetTempFileName), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or FILE_ATTRIBUTE_HIDDEN or FILE_FLAG_DELETE_ON_CLOSE, 0)); end; destructor TMemoryFileStream .Destroy; begin if (FHandle <> INVALID_HANDLE_VALUE) then begin FileClose(FHandle); end; inherited Destroy; end;
Delphi-Quellcode:
vorliegen hast und zeigst es dann an. Sobald der User jetzt das nächste Bild anzeigen will, speicherst du das aktuelle Bild mit der von mir geposteten Stream-Klasse, gibst das
TBitmap
Delphi-Quellcode:
frei und wiederholst die Aktionen zum Anzeigen des neuen Bildes. Soll nun eines der vorherigen Bilder angezeigt werden, lädst du das
TBitmap
Delphi-Quellcode:
einfach wieder aus dem Stream. Diese Stream-Klasse ist ziemlich performant, da sie den Windows File-Cache verwendet. Es sieht zwar so aus, als ob die Daten in einer temporären Datei gespeichert werden, aber so lange Windows global genug freien RAM zur Verfügung stehen hat, bleiben die Daten dort im Cache. Zugriffe auf die Festplatte hast du so also im Optimalfall gar nicht und als Bonus managed Windows den kompletten Speicher für dich.
TBitmap
Delphi-Quellcode:
Exceptions wären damit auf jeden Fall Geschichte.
EOutOfMemory
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Zitat:
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Süß! Windows 95! Delphi 3! Toll!
Aber auch die maximale Größe, die das Programm annimmt, zwingt mein System nicht in die Knie. Projekt öffnen und modifizieren habe ich mir allerdings gespart. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Delphi-Quellcode:
var
LMem : TMemoryManagerUsageSummary; begin GetMemoryManagerUsageSummary(LMem); Caption := LMem.AllocatedBytes.ToString + ' Bytes'; |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
![]() |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Ich habe erstmal Benedikt Magnus' Anregung aufgegriffen und die Größe der 7 Bitmaps ermittelt:
Delphi-Quellcode:
. Das ergab ungefähr 370 MB. Dann habe ich Bambinis Anregung umgesetzt, das ergab einen Wert von ca. 2,7 MB. Der Taskmanager zeigt ca. 500 MB. Das verstehe, wer will.
SizeOf(BMP) + Bmp.Width * Bmp.Height * (24 div 8)
Die Umsetzung von Zacherls Hinweis wird etwas aufwändiger und dauern. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Beispiel: Wenn ich ein Auto habe mit im Fahrzeugschein eingetragener Länge von 4,00 Meter: Ist das Auto 4,00 Meter lang oder ist es kürzer, weil ein LKW-Fahrer hinten draufgefahren ist? Memory-Leaks sind nicht der Normalfall. Redet das niemanden ein! ![]() In PSAPI.PAS:
Delphi-Quellcode:
_PROCESS_MEMORY_COUNTERS = record
cb: DWORD; PageFaultCount: DWORD; PeakWorkingSetSize: SIZE_T; WorkingSetSize: SIZE_T; QuotaPeakPagedPoolUsage: SIZE_T; QuotaPagedPoolUsage: SIZE_T; QuotaPeakNonPagedPoolUsage: SIZE_T; QuotaNonPagedPoolUsage: SIZE_T; PagefileUsage: SIZE_T; PeakPagefileUsage: SIZE_T; end; |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Wenn du also eine Pauschale für das Programm selbst nimmst und dir merkst, wie viele MB an Bitmaps bisher maximal gleichzeitig im Speicher lagen, müsstest du etwas erhalten, dass garantiert unter dem Wert im Taskmanager liegt (und bei voller oder konstanter Auslastung an Bitmaps sogar nahe am Taskmanager). |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
![]() |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Zitat:
Zitat:
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Zitat:
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
![]() Außerdem adressiert die diese API wieder nicht das eigentliche Problem. Also nochmal: Die großen Bitmaps erfordern große zusammenhängende Speicherblöcke im virtuellen Adressraum. Die Pages auszulagern entlastet aber nicht den virtuellen Adressraum, sondern nur den physikalischen Speicher. Deshalb kann ich auch nur nochmal empfehlen die ganze Herangehensweise mit Messen des aktuell benutzten Speichers so schnell wie möglich zu vergessen. Man kann einfach nicht zuverlässig vorhersagen ab welcher Größe ein
Delphi-Quellcode:
auftreten wird. Wie gesagt kann es sogar passieren, dass dein Prozess noch kaum Arbeitsspeicher verwendet, aber dank Fragmentierung kein genügend großer zusammenhängender Block verfügbar ist. Kannst du ganz leicht testen, indem du mal 1 GiB Speicher am Stück mit
EOutOfMemory
Delphi-Quellcode:
anforderst. Dieser Aufruf wird bei einem 32-Bit Programm in den meisten Fällen direkt scheitern. 1 GiB in mehreren kleinen 10 MiB Blöcken anzufordern, wird dahingegen sehr wahrscheinlich Erfolg haben. Auch wirst du merken, dass dein Bitmap Tool als 64-Bit Kompilat ohne Änderung vermutlich sehr viele Bitmaps mehr laden kann. Schlussfolgerung ist, dass nicht der physikalische RAM das Limit darstellt, sondern der virtuelle Adressraum.
GetMem
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Das mit den Forms mache ich teilweise auch so. Ich erstelle komplexe Formulare erst wenn sie wirklich benötigt werden.
Das verschnellert den Programmstart und hält den Speicherverbrauch ein wenig niedriger. Hat einzig den Nachteil der Usability im Sinne von "Wenn ich da drauf klicke, soll sofort was passieren" - ist dann eben nicht der Fall. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Jetzt habe ich Zacherls Vorschlag implementiert. Funktioniert auch. Aufgrund der Streams kann die Größe der Bitmaps jetzt genau angegeben werden. Der Taskmanager - der ja augenscheinlich erschütternd unzuverlässig ist - zeigt nun statt 500 MB nur noch 180 MB an.
Die ganze Geschichte läuft aber merklich langsamer ab als mit den direkt gespeicherten Bitmaps. Die Erstellung der Bitmaps/Streams ist in asychrone Threads ausgelagert, auch damit scheint es Probleme zu geben, das Programm hängt sich bei forcierter Belastung auf ("forcierte Belastung" heißt, ich halte die Weiter-Taste gedrückt und nudle 100 Bilder durch, dadurch müssen pausenlos Bilder geladen, gelesen und verworfen werden). Hab's jetzt aber noch nicht richtig handoptimiert, mal sehen. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Laut Zacherl müsste das alles im RAM liegen.
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Delphi-Quellcode:
dann nicht.
inherited Create(CreateFile(PChar(TPath.GetTempFileName), GENERIC_READ or GENERIC_WRITE,
0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or FILE_ATTRIBUTE_HIDDEN or FILE_FLAG_DELETE_ON_CLOSE, 0)); |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Zitat:
Delphi-Quellcode:
Flag. Solange dem System genug freier physikalischer zur Verfügung steht und nichts ausgelagert werden muss, befindet sich die Datei tatsächlich komplett im RAM.
FILE_ATTRIBUTE_TEMPORARY
Zitat:
Zitat:
Optimieren könntest du noch, indem du nach dem Laden des ersten Bildes im Hintergrund schon anfängst Nummer zwei (und evtl. danach noch Nummer 3) zu laden, etc. Sind sicher noch einige Verbesserungen drinnen. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Liste der Anhänge anzeigen (Anzahl: 1)
Schon mal an
![]() |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Zitat:
Delphi-Quellcode:
verwende, werden sogar alle BMP gleichzeitig geladen (und geht bei einer SSD richtig schnell, bei einer normalen Festplatte ist das eher kontraproduktiv). Dieses Laden geht bei Bitmaps viel schneller. Aber daran könnte ich wohl noch schrauben.
MaxThreads := 2 * System.CPUCount;
RAMDisk sieht sehr interessant aus, ist für meine Anwendung aber eher nichts (müsste erst eingerichtet werden, der Platzbedarf ändert sich dauernd mit den Einstellungen und der Größe der Bitmaps usw.). Aber für andere Sachen wäre das vielleicht wirklich interessant! PS: Ich habe jetzt in einer Schleife 100 Bilder einzeln geladen, dekomprimiert, in den Stream geladen und wieder freigegeben. Das dauerte etwa 37 Sekunden, also eine drittel Sekunde pro Bild. Das Wiederauslesen des Streams und Laden in ein Bitmap verlängerte die Prozedur um 5 Sekunden, also eine Zwanzigstelsekunde je Bitmap. Da scheint mir kaum Potenzial für Verbesserungen zu sein. Ich gucke mir nochmal das Management der Threads an. PPS: Das RAM-Disk-Tool ImDisk findet man jetzt ![]() ![]() PPPS: Von der RAM-Disk aus läuft der Prozess mit den 100 Bildern in 35 (!) Sekunden. Das scheint mir kein echtes Optimierungspotenzial. |
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Ich habe jetzt eine Reihe von Versuchen durchgeführt, die die alte (Speichern von TBitmap) mit der neuen (TMemoryFileStream) Methode vergleicht, mit einem etwas zwiespältigen Ergebnis.
|
AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
Mir kommt da grade noch eine Idee:
Dein größtes Problem ist ja erstmal die 2GiB Grenze des virtuellen Adressraums. Du könntest dir eine weitere (Stream-)Klasse basteln, in der du die Daten als Memory Mapped File ( ![]() ![]() ![]() Wenn du jetzt jeweils nur das aktuell geöffnete Bild mappst, hast du auch immer nur einen großen zusammenhängenden Speicherblock in deinem virtuellen Adressraum liegen und würdest zumindest diese Ursache für eine
Delphi-Quellcode:
Exception eliminieren. Sollte dein physikalischer RAM irgendwann nicht mehr ausreichen, fängt Windows halt an RAM in die Auslagerungsdatei auszulagern.
EOutOfMemory
Zu bedenken ist hierbei allerdings, dass du mit dieser Methode natürlich die Leistung des ganzen System beeinträchtigen kannst, wenn du den kompletten RAM mit deinen MMFs füllst. Wobei selbst hier der Overhead nicht allzu fatal sein sollte. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22: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