3

Wir haben eine D2007-Anwendung, deren Speicherbedarf unter Windows Server 2008 (x64, sp1) stetig zunimmt.
Es verhält sich normalerweise unter Windows Server 2003 (x32 oder x64), XP, etc ... wo es wie erwartet auf und ab geht.
Wir haben mit dem mitgelieferten Memory Manager oder dem neuesten FastMM4 4.92 mit den gleichen Ergebnissen versucht.Wird der Speicher für Delphi-Anwendungen, die unter Windows Server 2008 (SP1) ausgeführt werden, nicht zurückgewonnen?

Hat jemand versucht, die Speichernutzung von jeder Delphi-App auf Win2008 zu überwachen und würde bestätigen?
Oder hätten Sie eine Ahnung?

Precisions:
- keine Speicherlecks im herkömmlichen Sinne (und ja, ich bin sehr vertraut mit FastMM et al)
- Speichernutzung mit Process Explorer überwacht wurde; Sowohl der virtuelle Speicher (private Bytes) als auch der physische Speicher (WorkingSet Private) wachsen auf Win2008 - der Speicherverbrauch nahm immer weiter zu, auch wenn der Speicherdruck war. (so kamen wir, um zu untersuchen, wie es zu einem Fehler kam, aber nur auf Win2008 Boxen)

Update: der // ** reparierte ** // Code ist viel einfacher als unsere App, zeigt aber das gleiche Verhalten.
Erstellen einer Liste von 10.000.000 Objekten, dann 10.000.000 Schnittstellen, ausgeführt 2 mal wächst der verwendete Speicher um ~ 60MB und etwa 300MB mehr für 100 weitere Ausführungen auf Windows Server 2008, aber kehrt nur dahin zurück, wo es auf XP war.
Wenn Sie mehrere Instanzen starten, wird der Speicher nicht freigegeben, damit die anderen Instanzen ausgeführt werden können. Stattdessen wächst die Auslagerungsdatei und der Server kriecht ...

Update 2: QC report 73347
Nach einer weiteren Untersuchung sehen, haben wir es aufgespürt Kritische Abschnitte wie in der unten stehenden Code dargestellt.
Setzen Sie diesen Code in eine einfache VCL-Anwendung mit einem Button. Und überwachen mit Process Explorer:
es beginnt bei ~ 2,6 MB und nach 5 Läufen (klickt auf den Knopf) bleibt es bei ~ 118.6MB.
116MB in 5 Ausführungen verloren.

+0

Beziehen Sie sich auf die privaten Bytes, die virtuelle Größe oder das Working Set? Führen Sie den Process Explorer von SysInternals aus, um den Speicher zu überwachen, um eine bessere Vorstellung davon zu erhalten, was vor sich geht. http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx –

+0

Können Sie das Problem reproduzieren, wenn Sie TestMemoryInterfaces nicht aufrufen? – Alex

+0

Nun, ich fragte dies, weil Sie MyList.Add übergeben (TInterfaceList.Create()). Die Schnittstelle hat eine Referenznummer von null, wenn Konstruktor verlassen wird. Also kann es einen Platz für "schlechte Dinge" geben (sorry, ich habe kein Delphi zur Hand, um diese Vermutung zu bestätigen). Ich habe auf jeden Fall einen Bericht über QC über dieses Problem gesehen: Benutzer beschwert sich über mögliche versteckte Fehler in ähnlichem Code. Die Problemumgehung besteht darin, eine explizite Variable zu verwenden: I: = TInterfaceList.Create(); MyList.Add (I); – Alex

Antwort

3

Eigentlich hat Microsoft eine Änderung die Kritischen Abschnitte einige Debug-Informationen hinzuzufügen. Dieser Debug-Speicher wird erst am Ende der Anwendung freigegeben, aber irgendwie zwischengespeichert und wiederverwendet, weshalb er nach einiger Zeit plateaufähig ist.

Die Lösung, wenn Sie ohne das Gefühl, diese Erinnerung Strafe viel Kritische Abschnitte erstellen möchten ist die VCL-Code Patch Anrufe InitializeCriticalSection durch Aufrufe InitializeCriticalSectionEx und übergeben sie die Flagge CRITICAL_SECTION_NO_DEBUG_INFO ersetzen zu vermeiden die Erstellung der Debug-Struktur.

1

Haben Sie FastMM mit vollem Debug-Modus integriert? Gerade sind die FastMM4 Einheit direkt in Ihrem Projekt und

ReportMemoryLeaksOnShutdown := True 

gesetzt Wenn es nichts berichtet, vielleicht alles normalerweise beim Beenden des Programms freigegeben wird (vielleicht wegen der Referenzzählung). Sie könnten AQTime verwenden, um Speicher in Echtzeit zu überwachen. Mit dieser Anwendung können Sie die Bytes sehen, die für jeden Klassennamen und für den Rest des verwendeten Speichers "zählen". Vielleicht können Sie sehen, wer die Erinnerung nutzt. Die zeitlich begrenzte Demoversion reicht für diesen Job.

+0

Es gibt keine Lecks, es verhält sich normalerweise AUSSER mit Windows Server 2008. So muss es zwischen dem MM und dem OS sein. –

+0

Wenn die Anwendung nicht ausläuft, zeigt AQTime keine steigenden Zahlen an. Ich würde es versuchen, nur um sicher zu sein, dass es nicht deine App ist. –

+0

Wie würden Sie erklären, dass der gleiche Code mit allen Windows-Versionen außer Server 2008 funktioniert? Wäre lustig, wenn das OS Speicherlecks zu Ihren EXEs hinzufügen könnte. –

3

Es gibt ein neues sysinternals-Tool namens VMMap, das den zugewiesenen Speicher visualisiert. Vielleicht könnte es dir zeigen, was die großen Speicherblöcke sind.

1

Verweisen Sie auf die privaten Bytes, die virtuelle Größe oder das Working Set? Führen Sie Process Explorer from SysInternals aus, um den Speicher für eine bessere Vorstellung von dem, was vor sich geht, zu überwachen.

Ich habe keine spezifische Erfahrung mit diesem (obwohl ich 2008 x64 SP1 laufen, also könnte es testen), aber ich werde vorschlagen, dass Sie eine Testanwendung erstellen, die eine Menge Speicher zuweist und dann freigibt. Führen Sie Process Explorer from SysInternals aus, um den Speicher zu überwachen.

Wenn Sie testen Anwendung reproduziert das gleiche Verhalten versuchen Sie dann etwas Speicherdruck durch Zuweisen von Speicher in einem anderen Prozess - so viel, dass es fehlschlägt, wenn der zuvor freigegebene Speicher im ersten Prozess zurückgewonnen wird.

Wenn das weiterhin fehlschlägt, versuchen Sie einen anderen Speichermanager. Vielleicht ist es FastMM, das es tut.

+0

Hat eine Test App gemacht. Hat den Speicher unter Druck gesetzt. Versuchen Sie es mit einem anderen Speichermanager ... (saugt!) –

+0

Also hatte die Test-App das gleiche Verhalten? –

+0

Ja. Verfolgt eine Änderung in der Microsoft-Implementierung kritischer Abschnitte. Siehe Update 2. Danke! –

-1

Nun, die Speichernutzung kann steigen, auch wenn in Ihrer Anwendung kein Speicherleck vorhanden ist. In diesen Fällen besteht die Möglichkeit, dass Sie eine andere Ressource verlieren. Zum Beispiel, wenn Ihr Code, zum Beispiel, eine Bitmap zuweist und obwohl es alle Objekte freigibt, aber es schafft, die Fertigstellung einiger HBITMAP zu vergessen.

FastMM teilt Ihnen mit, dass Sie in Ihrer Anwendung keinen Speicherverlust haben, da Sie alle Ihre Objekte und Daten freigegeben haben. Aber Sie leckt immer noch andere Arten von Ressourcen (in meinem Beispiel - GDI-Objekte). Das Auslassen anderer Arten von Ressourcen kann sich auch auf Ihr Gedächtnis auswirken.

Ich schlage vor, Sie versuchen, ein anderes Tool, das nicht nur Speicherlecks, sondern auch andere Arten von Leckagen zu überprüfen. Ich denke, dass AQTime dazu in der Lage ist, aber ich bin mir nicht sicher.

Ein anderer möglicher Grund für dieses Verhalten ist Speicherfragmentierung. Angenommen, Sie haben 2000 Objekte von 1 MB Größe zugewiesen (vergessen wir eine Minute über MM-Overheads und das Vorhandensein anderer Objekte im Benutzerbereich). Jetzt haben Sie volle 2 GB ausgelasteter Speicher. Nehmen wir jetzt an, dass Sie alle geraden Objekte freigeben, also haben Sie jetzt Speicherplatz frei, wo 1 MB belegt und freie Blöcke gemischt sind. Sie haben jetzt zwar 1 GB freien Speicher, aber Sie können keinen Speicher für ein 2 MB-Objekt zuweisen, da die maximale Größe des freien Blocks nur 1 MB beträgt (aber Sie haben 1000 solcher Blöcke;)). Wenn die Speicher-Manager verwendet, um Blöcke größer als 1 MB für Ihre Objekte, dann ist es nicht die Speicherblocks zurück in OS freigeben kann, wenn Sie Ihre selbst Objekte befreit haben:

[ [busy] [free] [busy] [free] [busy] [free] ] 
[ [busy] [free] [busy] [free] [busy] [free] ]... 

Diese großen [...] Blöcke halb beschäftigt, also kann MM sie OS nicht geben. Wenn Sie einen anderen Block fragen werden, die> 1 Mb dann MM benötigen noch einen weiteren Block von OS zuzuordnen:

[ [busy] [free] [busy] [free] [busy] [free] ] 
[ [busy] [free] [busy] [free] [busy] [free] ]... 
[ [your-new-object] [free.................] ] 

Beachten Sie, dass diese nur Beispiele für incresing Speichernutzung sind, wenn Sie nicht haben Speicherleck. Ich sage nicht, dass Sie die genaue Situation haben: D

+0

Wie funktioniert diese Adressierung, dass der gleiche Code mit allen Windows-Versionen außer Server 2008 funktioniert? –

+0

Warum nicht? Wenn Code eine Art von Implementierungsdetails verwendet, die in 2008/Vista geändert wurden, warum nicht? Ich meine deinen Code und Delphi's MM. – Alex

+0

Dies ist nicht so sehr ein Problem der Speicherfragmentierung, sondern der Adressraumfragmentierung. –

1

Überprüfen Sie, ob Sie this issue haben (das ist ein anderes Problem, unabhängig von dem, die ich in den Kommentaren zu Ihrer Frage erwähnt habe).

0

Zusätzlich zu Alexander wird dies normalerweise "Heap Fragmentierung" genannt.

Beachten Sie, dass FastMM insgesamt widerstandsfähiger und schneller sein sollte, aber wenn die ursprüngliche App für den D7-Memmanager optimiert wurde, könnte FastMM tatsächlich schlechter abschneiden.

1

Ich habe diesen Code, um dieses Problem in meinen Anwendungen zu beheben. Ist der gleiche Fall von FastCode, müssen Sie die Einheit als die erste Einheit Ihres Projekts setzen, um die Fehlerbehebung auszuführen. Wie die uRedirecionamentos in diesem Fall: enter image description here

unit uCriticalSectionFix; 
// By Rodrigo F. Rezino - [email protected] 

interface 

uses 
    Windows; 

implementation 

uses 
    SyncObjs, SysUtils; 

type 
    InitializeCriticalSectionExProc = function(var lpCriticalSection: TRTLCriticalSection; dwSpinCount: DWORD; Flags: DWORD): BOOL; stdcall; 

var 
    IsNewerThenXP: Boolean; 
    InitializeCriticalSectionEx: InitializeCriticalSectionExProc; 

type 
    PJump = ^TJump; 
    TJump = packed record 
    OpCode: Byte; 
    Distance: Pointer; 
    end; 

    TCriticalSectionHack = class(TSynchroObject) 
    protected 
    FSection: TRTLCriticalSection; 
    public 
    constructor Create; 
    end; 

function GetMethodAddress(AStub: Pointer): Pointer; 
const 
    CALL_OPCODE = $E8; 
begin 
    if PBYTE(AStub)^ = CALL_OPCODE then 
    begin 
    Inc(Integer(AStub)); 
    Result := Pointer(Integer(AStub) + SizeOf(Pointer) + PInteger(AStub)^); 
    end 
    else 
    Result := nil; 
end; 

procedure AddressPatch(const ASource, ADestination: Pointer); 
const 
    JMP_OPCODE = $E9; 
    SIZE = SizeOf(TJump); 
var 
    NewJump: PJump; 
    OldProtect: Cardinal; 
begin 
    if VirtualProtect(ASource, SIZE, PAGE_EXECUTE_READWRITE, OldProtect) then 
    begin 
    NewJump := PJump(ASource); 
    NewJump.OpCode := JMP_OPCODE; 
    NewJump.Distance := Pointer(Integer(ADestination) - Integer(ASource) - 5); 

    FlushInstructionCache(GetCurrentProcess, ASource, SizeOf(TJump)); 
    VirtualProtect(ASource, SIZE, OldProtect, @OldProtect); 
    end; 
end; 

procedure OldCriticalSectionMethod; 
asm 
    call TCriticalSection.Create; 
end; 

{ TCriticalSectionHack } 

const 
    CRITICAL_SECTION_NO_DEBUG_INFO = $01000000; 
    NEW_THEN_XP = 6; 

constructor TCriticalSectionHack.Create; 
begin 
    inherited Create; 
    if IsNewerThenXP then 
    InitializeCriticalSectionEx(FSection, 0, CRITICAL_SECTION_NO_DEBUG_INFO) 
    else 
    InitializeCriticalSection(FSection); 
end; 

procedure AdjustMethod; 
var 
    LKernel32: HModule; 
begin 
    if IsNewerThenXP then 
    begin 
    LKernel32 := LoadLibrary('kernel32.dll'); 
    @InitializeCriticalSectionEx := GetProcAddress(LKernel32, 'InitializeCriticalSectionEx'); 
    end; 
end; 

initialization 
    AddressPatch(GetMethodAddress(@OldCriticalSectionMethod), @TCriticalSectionHack.Create); 
    IsNewerThenXP := CheckWin32Version(NEW_THEN_XP, 0); 
    AdjustMethod; 


end. 
+0

Ein Problem unter XP wurde behoben –

Verwandte Themen