![]() |
Speicherbenutzung nach wochenlanger Benutzung
Hallo zusammen,
ich habe ein Problem und hoffe ich kann es einigermaßen verständlich rüberbringen und ihr könnt mir ein paar Tipps geben :-) Wenn unsere Anwendung mehrere Wochen durchgängig läuft (2-3 Wochen), hat man immer folgendes Erscheinungsbild. Der Speicher wächst kontinuierlich an und bei 1,55GB ist Schluss (ich weiß, dass nur max 2GB für eine Anwendung zur Verfügung stehen)... Das 3GB Flag würde das Problem wahrscheinlich nur zeitlich nach "hinten" schieben. Ausgelesen wird der Speicher wie folgt:
Delphi-Quellcode:
Den Quelltext kann ich leider nicht posten (Firmen-Quelltext). Ich kann euch aber bisschen erklären wie die Sachen so ablaufen in der Anwendung.
function GetRAMUsage: Int64;
var pmc : PPROCESS_MEMORY_COUNTERS; MemWert : Int64; cb : Integer; begin MemWert := 0; cb := SizeOf(_PROCESS_MEMORY_COUNTERS); GetMem(pmc, cb); try pmc^.cb := cb; if GetProcessMemoryInfo(GetCurrentProcess(), pmc, cb) then begin MemWert := pmc^.WorkingSetSize; end; finally FreeMem(pmc); end; Result := MemWert; end; Verwendet wird Win7 sowohl 32bit als auch 64bit mit Delphi 2009. 1) Es gibt eine Menge Threads, die sich alle untereinander über die Thread-Message-Queue unterhalten. Jeder Thread prüft seine Nachrichten Warteschlange und löscht dort die Nachrichten raus, wenn welche eingetroffen sind. 2) Wir arbeiten viel mit dem Typ TList und haben davon ca. 50 Stück im Einsatz. Enthalten sind natürlich Zeiger darin. In einigen Listen ist immer viel "Bewegung" vorhanden (also Einträge löschen, hinzufügen usw. -> natürlich immer mit New/Dispose). Einige Listen sammeln für ein ganzen Tag Daten und beim Datumswechsel (Tagwechsel) werden diese Listen mit einem Rutsch freigegeben und die Liste gelöscht. Jede Liste hat auch eine unterschiedliche Datenstrukturgröße die sie verwaltet. Ich bin schon die ganze Zeit auf der Suche ob da ein Speicherleck oder ähnliches vorhanden ist aber ich finde einfach nix. Kann es auch sein, dass einfach mit der Zeit, der Speicher fragmentiert und damit keine größeren zusammenhängenden Blöcke Speicher reserviert werden können? Das Erscheinungsbild ist auch irgendwie so, dass ca. 2Wochen der Speicher recht konstant bleibt und dann innerhalb von 2-3h der Speicher bis auf die erwähnten 1,55GB ansteigt obwohl keine andere Bedienhandlung oder ähnliches ausgeführt wurde. Ich hoffe ihr habt einfach paar Tipps oder Ideen, wo bzw. wie ich weitersuchen kann. Danke im voraus Edit: Nachtrag: Ich habe zum Beispiel auch eine Liste, die 5MB groß ist für einen Tag. Beim Tageswechsel geht eine Schleife über die ganze Liste und "Disposed" die Items und löscht die Liste am Ende. Allerdings sehe ich im TaskManager nicht, dass diese 5MB wirklich wieder frei sind, der Speicher der Anwendung bleibt bei dem gleichen Wert. Mit "Speicher" meine ich immer den Speicher welchen wie oben ausgelesen wird... |
AW: Speicherbenutzung nach wochenlanger Benutzung
Zitat:
Der Wert, den du ausliest, sagt nicht so viel über den aktuellen Speicherverbrauch aus. Das Working-Set wird reduziert, wenn andere Prozesse den Speicher benötigen. Wenn du Speicherlecks suchen willst, solltest du bessere ![]() Afaik kannst du dir auch von FastMM die Speicherlecks bei Programmende anzeigen lassen. |
AW: Speicherbenutzung nach wochenlanger Benutzung
Ok, was wäre denn der richtige Wert für den Speicherbedarf?
Das Erscheinungsbild ist, dass wenn dieser Wert (WorkingSetSize) bei 1,55GB ist, dass dann in der Anwendung alles möglich in die "Hose" geht. Ich denke wegen der 2GB Grenze pro Anwendung... Ich gucke mir FastMM an, hoffe ich kann irgendwie was feststellen. Gruß |
AW: Speicherbenutzung nach wochenlanger Benutzung
![]()
Delphi-Quellcode:
wenn fast FastMM schon im Delphi integriert ist.
ReportMemoryLeaksOnShutdown := True;
![]() früher gab es noch ![]() ![]() ![]() [add] Das Ding hatte ich vor Langem mal zusammengehackt. (wegen der Recordfunktionen sollte es wohl ab D2006/TDE laufen) - mit .Init initialisieren - z.B. alle 10 Sekunden .Update aufrufen - und alle 5 Minuten .Execute (den Result-String in eine Logdatei)
Delphi-Quellcode:
(als Zitat sieht der Code hübscher aus)
type
TProcessInfoX = record IntStart, IntCount: Cardinal; // GetTickCount // Process HandlesSum, HandlesMin, HandlesMax: Cardinal; // GetProcessHandleCount KernelTimeStart, KernelTimeLast, KernelTimeMin, KernelTimeMax: Int64{TFileTime}; // GetProcessTimes(lpKernelTime) UserTimeStart, UserTimeLast, UserTimeMin, UserTimeMax: Int64{TFileTime}; // GetProcessTimes(lpUserTime) MemorySum, MemoryMin, MemoryMax: UInt64; // GlobalMemoryStatusEx(ullAvailVirtual) // Global MemoryLoadSum, MemoryLoadMin, MemoryLoadMax: Cardinal; // GlobalMemoryStatusEx(dwMemoryLoad) FreeRAMSum, FreeRAMMin, FreeRAMMax: UInt64; // GlobalMemoryStatusEx(ullAvailPhys) PageFileSum, PageFileMin, PageFileMax: UInt64; // GlobalMemoryStatusEx(ullAvailPageFile) // procedure Init; // bei Programmstart initialisieren procedure Update; // zwischendurch updaten, für die Min/Max-Berechnung function Execute: String; // am Ende eines Messintervalls alles Berechnen und neues Intervall beginnen end; { TProcessInfoX } procedure TProcessInfoX.Init; begin FillChar(Self, SizeOf(Self), 0); IntStart := GetTickCount; IntCount := 1; Execute; end; procedure TProcessInfoX.Update; var H: THandle; Handles: Cardinal; KernelTime, UserTime, X: Int64{TFileTime}; _Time: TFileTime; MemoryStatus: TMemoryStatusEx; begin Inc(IntCount); H := GetCurrentProcess; if GetProcessHandleCount(H, Handles) then begin Inc(HandlesSum, Handles); if HandlesMin > Handles then HandlesMin := Handles; if HandlesMax < Handles then HandlesMax := Handles; end; if GetProcessTimes(H, _Time, _Time, TFileTime(KernelTime), TFileTime(UserTime)) then begin X := KernelTime - KernelTimeLast; KernelTimeLast := KernelTime; if KernelTimeMin > X then KernelTimeMin := X; if KernelTimeMax < X then KernelTimeMax := X; X := UserTime - UserTimeLast; UserTimeLast := UserTime; if UserTimeMin > X then UserTimeMin := X; if UserTimeMax < X then UserTimeMax := X; end; MemoryStatus.dwLength := SizeOf(MemoryStatus); if GlobalMemoryStatusEx(MemoryStatus) then with MemoryStatus do begin X := ullTotalVirtual - ullAvailVirtual; Inc(MemorySum, X); if MemoryMin > X then MemoryMin := X; if MemoryMax < X then MemoryMax := X; Inc(MemoryLoadSum, dwMemoryLoad); if MemoryLoadMin > dwMemoryLoad then MemoryLoadMin := dwMemoryLoad; if MemoryLoadMax < dwMemoryLoad then MemoryLoadMax := dwMemoryLoad; X := ullAvailPhys; Inc(FreeRAMSum, X); if FreeRAMMin > X then FreeRAMMin := X; if FreeRAMMax < X then FreeRAMMax := X; X := ullTotalPageFile - ullAvailPageFile; Inc(PageFileSum, X); if PageFileMin > X then PageFileMin := X; if PageFileMax < X then PageFileMax := X; end; end; function TProcessInfoX.Execute: String; function RD(i: UInt64): Integer; inline; begin Result := (i + 787200) shr 20; end; var H: THandle; _Time: TFileTime; KernelTime, UserTime: Int64{TFileTime}; KernelTimeMean, UserTimeMean: Int64{TFileTime}; Handles, HandlesMean, MemoryLoadMean: Cardinal; ProcessLoad, ProcessUserT, IntTime: Cardinal; MemoryMean, FreeRAMMean, PageFileMean: UInt64; MemoryStatus: TMemoryStatusEx; New: TProcessInfoX; MMS: TMemoryManagerState; UsedDelphiMM, ReservedDelphiMM: Cardinal; i: Integer; begin if (GetTickCount - IntStart) > 100 then Update; IntTime := GetTickCount - IntStart; if IntTime <= 0 then IntTime := 1; // kein DivByZZero H := GetCurrentProcess; FillChar(New, SizeOf(Self), 0); New.IntStart := GetTickCount; New.IntCount := 1; if GetProcessHandleCount(H, Handles) then begin HandlesMean := HandlesSum div IntCount; New.HandlesSum := Handles; New.HandlesMin := Handles; New.HandlesMax := Handles; end else HandlesMean := 0; if GetProcessTimes(H, _Time, _Time, TFileTime(KernelTime), TFileTime(UserTime)) then begin KernelTimeMean := KernelTime - KernelTimeStart; UserTimeMean := UserTime - UserTimeStart; ProcessLoad := ((UserTimeMean + KernelTimeMean) div 100) div IntTime; ProcessUserT := ( UserTimeMean div 100) div IntTime; New.KernelTimeStart := KernelTime; New.UserTimeStart := UserTime; New.KernelTimeLast := KernelTime; New.UserTimeLast := UserTime; New.KernelTimeMin := High(Int64); New.UserTimeMin := High(Int64); New.KernelTimeMax := 0; New.UserTimeMax := 0; end else begin ProcessLoad := 0; ProcessUserT := 0; end; MemoryStatus.dwLength := SizeOf(MemoryStatus); if GlobalMemoryStatusEx(MemoryStatus) then begin with MemoryStatus do begin MemoryMean := MemorySum div IntCount; MemoryLoadMean := MemoryLoadSum div IntCount; FreeRAMMean := FreeRAMSum div IntCount; PageFileMean := PageFileSum div IntCount; New.MemorySum := ullTotalVirtual - ullAvailVirtual; New.MemoryLoadSum := dwMemoryLoad; New.FreeRAMSum := ullAvailPhys; New.PageFileSum := ullTotalPageFile - ullAvailPageFile; New.MemoryMin := New.MemorySum; New.MemoryLoadMin := New.MemoryLoadSum; New.FreeRAMMin := New.FreeRAMSum; New.PageFileMin := New.PageFileSum; New.MemoryMax := New.MemorySum; New.MemoryLoadMax := New.MemoryLoadSum; New.FreeRAMMax := New.FreeRAMSum; New.PageFileMax := New.PageFileSum; end; end else begin MemoryMean := 0; MemoryLoadMean := 0; FreeRAMMean := 0; PageFileMean := 0; end; GetMemoryManagerState(MMS); UsedDelphiMM := MMS.TotalAllocatedMediumBlockSize + MMS.TotalAllocatedLargeBlockSize; For i := 0 to NumSmallBlockTypes - 1 do Inc(UsedDelphiMM, MMS.SmallBlockTypeStates[i].InternalBlockSize * MMS.SmallBlockTypeStates[i].AllocatedBlockCount); ReservedDelphiMM := MMS.ReservedMediumBlockAddressSpace + MMS.ReservedLargeBlockAddressSpace; Result := Format('CPU: %2d %% (%2d) Memory: %4d MB (%4d-%4d) MemoryLoad: %2d %% (%2d-%2d) DelphiMM: %4d MB (res %3d MB) ' + 'FreeRAM: %4d MB (%4d-%4d) PageFile: %4d MB (%4d-%4d) Handles: %3d (%3d-%3d)', [ ProcessLoad, ProcessUserT, RD(MemoryMean), RD(MemoryMin), RD(MemoryMax), MemoryLoadMean, MemoryLoadMin, MemoryLoadMax, RD(UsedDelphiMM), RD(ReservedDelphiMM), RD(FreeRAMMean), RD(FreeRAMMin), RD(FreeRAMMax), RD(PageFileMean), RD(PageFileMin), RD(PageFileMax), HandlesMean, HandlesMin, HandlesMax]); Self := New; end; |
AW: Speicherbenutzung nach wochenlanger Benutzung
Hallo himitsu,
gibts die Funktion "GetProcessHandleCount" unter Delphi 2009 (Win7) nicht (mehr)? Wenn nein, welche könnte ich als Alternative nutzen? Gruß |
AW: Speicherbenutzung nach wochenlanger Benutzung
Siehe MSDN:
![]() Übersetzung (scheint zu funktionieren):
Delphi-Quellcode:
uses windows;
//... function GetProcessHandleCount(hProcess: THandle; var pdwHandleCount: DWORD): BOOL; stdcall; external 'kernel32.dll'; |
AW: Speicherbenutzung nach wochenlanger Benutzung
ReportMemoryLeaksOnShutDown hat ein Defizit: es zeigt Dir tatsächlich nur Leaks an. Handelt es sich jedoch um steigenden Speicherbedarf welcher bei Programmende sauber abgeräumt wird, so hilft dir das Ganze kein Stück weiter. Genau dafür habe ich ein Tool gebaut (Infos finden sich unter
![]() Wenn Du magst, melde Dich per PM und ich kann Dir kurzfristig was zur Verfügung stellen. |
AW: Speicherbenutzung nach wochenlanger Benutzung
Hallo BUG,
MSDN habe ich gelesen nur leider gibt es in meiner Windows.pas die Funktion "GetProcessHandleCount" nicht. Unit ist eingebunden aber immernoch "undeklarierter Bezeichner". Hallo sahimba, da hast du Recht mit dem ReportMemoryLeaks. Ich werde erstmal versuchen die Sache oben zum Laufen zu bekommen und melde mich bei Gelegenheit nochmal bei dir. Danke Grüße |
AW: Speicherbenutzung nach wochenlanger Benutzung
Zitat:
Zitat:
|
AW: Speicherbenutzung nach wochenlanger Benutzung
Augen auf beim Eier-kauf ;-)
Danke, habe ich irgendwie nicht für "voll" genommen :-) Grüße |
AW: Speicherbenutzung nach wochenlanger Benutzung
Hallo Himitsu,
anbei mal ein Ausschnitt des Strings, welcher mir nach Aufruf von Execute zurückgeliefert wird: CPU: 0 % ( 0) Memory: 542 MB ( 542- 542) MemoryLoad: 67 % (67-67) DelphiMM: 96 MB (res 15 MB) FreeRAM: 1310 MB (1309-1312) PageFile: 3138 MB (3128-3147) Handles: 15995 (15995-15995) Da steht bei Memory: 542 MB Laut TaskManager verbraucht das Programm aber nur 157 MB. Hast du eine Idee? MemoryLoad: Ist das die Größe der Anwendung im Gesamtspeicher oder auf was bezieht sich die %-Zahl? Gruß |
AW: Speicherbenutzung nach wochenlanger Benutzung
"Memory" gibt den komplett belegten Speicherbereich an, innerhalb des virtuellen Speichers deiner Anwendung,
also inklusive MMFs und anderer verlinkter Speicherblöcke, wie z.B. die Codes der EXE und DLLs. Dazu dann auch noch reservierte Speicherblöcke und Blockanteile, wo aber nichts drin ist, welche aber dennoch nicht zur allgemeinen Nutzung verfügbar sind. Dein Taskmanager zeigt nur den RAM, welchen es mit dem RAM oder der PageFile verlinkt hat. (nichts Reserviertes und keine MMFs, welche mit Speicher anderer Anwendungen oder mit einer Datei verunden sind) Jupp, MemoryLoad ist aufr den gesamten Arbietsspeicher des System bezogen. |
AW: Speicherbenutzung nach wochenlanger Benutzung
Also um das nochmal irgendwie zusammen zu bekommen... :-)
Die Anwendung macht ja nach einigen Wochen schlapp, weil ich wie in Post 1 geschrieben habe, an die 1,5 GB Speicher ran komme. Das was in "Memory" von deinem String steht ist nicht der Speicher von meiner Anwendung sondern von allen Anwendungen oder? Woher sonst der Unterschied zwischen 157MB Taskmanager (WorkingSetSize liegt das gleiche) und 542MB?! Ich werde in den String noch den tatsächlichen Speicherverbrauch der Anwendung aufnehmen... Sagt mal, wenn ich jetzt feststelle das nach ca. einer Woche der Speicher angestiegen ist (z.B. auf 1GB), gibt es dann Möglichkeiten herauszubekommen, welches Formular, welcher Thread oder welche Klasse wieviel Speicher wegnimmt? Damit ich irgendwie dahinterkommen kann wer sich diesen Speicher nimmt. Nur zur Info: Die Anwendung hat ca. 200 Threads und an die 60 Formulare (welche teilweise dynamisch als auch statisch erzeugt werden). Deswegen würde ich natürlich gerne wissen, wer der Übeltäter ist... Gruß |
AW: Speicherbenutzung nach wochenlanger Benutzung
Nee, Memory ist nur der virtuelle/private Speicher deiner Anwendung.
MemoryLoad, FreeRAM und PageFile ist von allen Anwendungen, bzw. vom ganzen System. direkt festlegen kann man das so nicht direkt. FastMM kann man irgendwie in einen FullDebugModus versetzen ... ich bin mir nicht sicher, aber ich glaub da merkt der sich wer (Zeit, Code-Adresse und eventuell Thread) jeden Speicherblock reserviert hat. Man könnte nun ein Snapshot vom Speicher machen (sich eine Liste aller Blöcke merken) und diesen dann vergleichen. Und über FastMM bekommst du auch nur Speicher, welcher darüber reserviert wurde. VirtuallAlloc müßte man z.B. hooken, um das auch noch mitzubekommen. Schlimm sind solche falschen Speicherlecks. z.B. könntest du über einen Timer TStringGrid.Create(Self) aufrufen, was stückchenweise speicher verschlingt, aber beim Beenden des Programms gibt der Owner (die Form) dieses "Speicherlecks" frei und FastMM bekommt davon garnicht mit, daß es "eigentlich" Speicherlecks waren. |
AW: Speicherbenutzung nach wochenlanger Benutzung
Zitat:
![]() Snapshots sind dort auch bereits (rudimentär) integriert. Weiter gibt es eine "Statistik" in welcher angezeigt wird, von welcher Klasse wie viele Objekte bisher erzeugt worden sind, wie viele derzeit noch "leben" etc. Der Callstack der Erzeugung kann angezeigt werden, und das ganze eben live. Das Ganze funktioniert auch für mehrere Threads. |
AW: Speicherbenutzung nach wochenlanger Benutzung
Zitat:
|
AW: Speicherbenutzung nach wochenlanger Benutzung
Zitat:
![]() Wie gesagt... die Version auf der Webseite ist vollkommen veraltet, ich habe aber eine ganz frische bei mir die ich dann irgendwann auch auf die freie Wildbahn herauslassen werde. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:49 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