Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Speicherbenutzung nach wochenlanger Benutzung (https://www.delphipraxis.net/168800-speicherbenutzung-nach-wochenlanger-benutzung.html)

Alex_ITA01 12. Jun 2012 10:37

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:
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;
Den Quelltext kann ich leider nicht posten (Firmen-Quelltext). Ich kann euch aber bisschen erklären wie die Sachen so ablaufen in der Anwendung.
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...

BUG 12. Jun 2012 10:59

AW: Speicherbenutzung nach wochenlanger Benutzung
 
Zitat:

Zitat von Alex_ITA01 (Beitrag 1170402)
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)...

Ausgelesen wird der Speicher wie folgt:
Delphi-Quellcode:
// ...
    if GetProcessMemoryInfo(GetCurrentProcess(), pmc, cb) then
    begin
      MemWert := pmc^.WorkingSetSize;
    end;
//...

Eigentlich ist es nicht schlimm, wenn eine Anwendung ein großes Working-Set von Windows zugeteilt bekommt, im Gegenteil: weniger Swappen.
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 Tools verwenden.
Afaik kannst du dir auch von FastMM die Speicherlecks bei Programmende anzeigen lassen.

Alex_ITA01 12. Jun 2012 11:20

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ß

himitsu 12. Jun 2012 11:48

AW: Speicherbenutzung nach wochenlanger Benutzung
 
MSDN-Library durchsuchenGlobalMemoryStatusEx (ullAvailVirtual) oder du rechnest ein bissl mit den Werten rum, welche die FastMM (der DelphiMM) liefert.

Delphi-Quellcode:
ReportMemoryLeaksOnShutdown := True;
wenn fast FastMM schon im Delphi integriert ist.

Delphi-Referenz durchsuchenGetMemoryManagerState
früher gab es noch Delphi-Referenz durchsuchenAllocMemCount, Delphi-Referenz durchsuchenAllocMemSize und Delphi-Referenz durchsuchenGetHeapStatus



[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:
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;
(als Zitat sieht der Code hübscher aus)

Alex_ITA01 20. Jun 2012 12:23

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ß

BUG 20. Jun 2012 12:49

AW: Speicherbenutzung nach wochenlanger Benutzung
 
Siehe MSDN: GetProcessHandleCount

Übersetzung (scheint zu funktionieren):
Delphi-Quellcode:
uses windows;
//...
function GetProcessHandleCount(hProcess: THandle; var pdwHandleCount: DWORD): BOOL; stdcall; external 'kernel32.dll';

sahimba 20. Jun 2012 12:55

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 http://ddobjects.de/ddserver). Die auf der Seite bereitstehende Version ist veraltet, habe mich lange nicht drum gekümmert, ich bin aber just dabei diese zu aktuelisieren (XE2 32bit wird bereits unterstützt, einige neue Features sind hinzugekommen etc.).
Wenn Du magst, melde Dich per PM und ich kann Dir kurzfristig was zur Verfügung stellen.

Alex_ITA01 20. Jun 2012 12:59

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

BUG 20. Jun 2012 13:02

AW: Speicherbenutzung nach wochenlanger Benutzung
 
Zitat:

Zitat von Alex_ITA01 (Beitrag 1171751)
MSDN habe ich gelesen nur leider gibt es in meiner Windows.pas die Funktion "GetProcessHandleCount" nicht. Unit ist eingebunden aber immernoch "undeklarierter Bezeichner".

Deshalb habe ich dir ja die Deklaration übersetzt :wink:
Zitat:

Zitat von BUG (Beitrag 1171746)
Delphi-Quellcode:
function GetProcessHandleCount(hProcess: THandle; var pdwHandleCount: DWORD): BOOL; stdcall; external 'kernel32.dll';

Einfach in deinen Code übernehmen.

Alex_ITA01 20. Jun 2012 13:14

AW: Speicherbenutzung nach wochenlanger Benutzung
 
Augen auf beim Eier-kauf ;-)
Danke, habe ich irgendwie nicht für "voll" genommen :-)

Grüße


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:19 Uhr.
Seite 1 von 2  1 2      

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