Ist der Speicher nicht für Delphi-Anwendungen auf Windows Server 2008 (SP1) freigegeben?
-
13-09-2019 - |
Frage
Wir haben eine D2007 Anwendung, deren Speicherbedarf wächst stetig, wenn sie auf Windows Server 2008 (x64, SP1) ausgeführt wird.
Es verhält sich normal auf Windows Server 2003 (x32 oder x64), XP, etc ... wo es rauf und runter geht wie erwartet.
Wir haben mit dem mitgelieferten Memory Manager oder die neuesten FastMM4 4.92 mit dem gleichen Ergebnis ausprobiert.
Hat jemand versucht, die Speichernutzung aller Delphi-App auf Win2008 zu überwachen und bestätigen würde?
Oder würde keine Ahnung haben?
Precisions:
- keine Speicherlecks im gesunden Menschenverstand (und ja ich sehr vertraut bin mit FastMM et al)
- Speicherverbrauch wurde mit Process Explorer überwacht; beide virtuellen Speicher (Private Bytes) und Physical Memory (WorkingSet Private) wachsen auf Win2008
- Speicherverbrauch immer noch wächst auch bei Speicherdruck war. (Das ist, wie wir gekommen ist, zu untersuchen, wie es einen Fehler verursacht hat, sondern nur auf Win2008-Box)
Aktualisieren :. Die // ** repaced ** // Code viel einfacher als unsere App ist aber zeigt das gleiche Verhalten
Erstellen einer Liste von 10 Millionen Objekte dann 10.000.000 Schnittstellen, ausgeführt 2mal wächst den verwendeten Speicher von ~ 60 MB und etwa mehr 300MB für 100 weitere Ausführungen auf Windows Server 2008, aber nur zurück, wo es war auf XP.
Wenn Sie mehrere Instanzen starten, wird der Speicher nicht den anderen Instanzen zu ermöglichen freigegeben auszuführen. Stattdessen wächst die Auslagerungsdatei und der Server kriecht ...
Update 2 : siehe QC melden 73347
Nach einer weiteren Untersuchung haben wir es verfolgt Kritische Abschnitte nach unten , wie in der unten stehenden Code dargestellt.
Setzen Sie diesen Code in eine einfache VCL-Anwendung mit einem Knopf. Und Monitor mit Process Explorer:
es beginnt bei ~ 2,6 MB und nach 5 Läufen (klickt auf die Schaltfläche) bleibt es bei ~ 118.6MB.
116MB verloren in fünf Ausführungen.
//***********************
const
CS_NUMBER = 10000000;
type
TCSArray = Array[1..CS_NUMBER] of TRTLCriticalSection;
PCSArray = ^TCSArray;
procedure TestStatic;
var
csArray: PCSArray;
idx: Integer;
begin
New(csArray);
for idx := 1 to length(csArray^) do
InitializeCriticalSection(csArray^[idx]);
for idx := 1 to length(csArray^) do
DeleteCriticalSection(csArray^[idx]);
Dispose(csArray);
end;
procedure TestDynamic(const Number: Integer);
var
csArray: array of TRTLCriticalSection;
idx: Integer;
begin
SetLength(csArray, Number);
for idx := Low(csArray) to High(csArray) do
InitializeCriticalSection(csArray[idx]);
for idx := Low(csArray) to High(csArray) do
DeleteCriticalSection(csArray[idx]);
end;
procedure TForm4.Button1Click(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
TestStatic;
TestDynamic(CS_NUMBER);
end;
Lösung 2
Eigentlich hat Microsoft eine Änderung die Kritischen Abschnitte einige Debug-Informationen hinzuzufügen. Dieser Debug-Speicher wird nicht bis zum Ende der Anwendung freigegeben, aber irgendwie zwischengespeichert und wiederverwendet, weshalb nach einer Weile es Plateau kann.
Die Lösung, wenn Sie ohne das Gefühl, diese Erinnerung Strafe viel Kritische Abschnitte erstellen möchten ist die VCL-Code Patch zu ersetzen Anrufe InitializeCriticalSection
durch Anrufe auf InitializeCriticalSectionEx
und geben sie die Fahne CRITICAL_SECTION_NO_DEBUG_INFO
die Schaffung der Debug-Struktur zu vermeiden.
Andere Tipps
Es gibt ein neues Sysinternals-Tool namens VMMap die die visualisiert zugewiesenen Speicher. Vielleicht könnte es Ihnen zeigen, was die großen Speicherblöcke sind.
Haben Sie umfassen FastMM mit voller Debug-Modus? Gerade ist die FastMM4 Einheit direkt in Ihrem Projekt und setzt
ReportMemoryLeaksOnShutdown := True
Wenn es nichts berichtet ist, vielleicht ist alles normal beim Beenden des Programms befreit (vielleicht wegen der Referenzzählung). Sie könnten verwenden AQTime Speicher in Echtzeit zu überwachen. Mit dieser Anwendung können Sie das Bytes sehen für jede Klasse Namen und für Rest des verwendeten Speichers „Zählen“. Vielleicht können Sie sehen, wer den Speicher verwendet. Die zeitlich begrenzte Demoversion ist genug für diesen Job.
Beziehen Sie sich auf den Private Bytes, virtuelle Größe oder dem Working Set? Führen Sie Process Explorer von Sysinternals den Speicher für eine bessere Idee zu überwachen von was los ist.
Ich habe keine spezielle Erfahrung mit diesem (obwohl ich laufe 2008 x64 SP1, so könnte es testen), aber ich werde vorschlagen, dass Sie eine Testanwendung erstellen, die eine Reihe von Speicher zuordnet und sie dann freizugeben. Führen Sie Process Explorer von Sysinternals den Speicher zu überwachen.
Wenn Sie testen Anwendung das gleiche Verhalten reproduziert dann versuchen, einigen Speicherdruck erzeugt durch den Speicher in einem anderen Prozess Zuteilung -. So sehr, dass es scheitern wird, es sei denn, dass zuvor freigegebene Speicher in dem ersten Prozess freigegeben wird
Wenn das weiterhin fehlschlägt, versuchen Sie einen anderen Speicher-Manager. Vielleicht ist es FastMM, dass es tut.
Überprüfen Sie, ob Sie dieses Problem (dies ist ein anderes Thema , in keinem Zusammenhang mit dem einem, die ich in den Kommentaren zu Ihrer Frage erwähnt habe).
habe ich diesen Code, um dieses Problem auf meinen Anwendungen zu korrigieren.
Ist der gleiche Fall von FastCode, um das Update zu Lauf machen Sie müssen das Gerät als erstes Gerät Ihres Projekts zu stellen.
Wie die uRedirecionamentos in diesem Fall:
unit uCriticalSectionFix;
// By Rodrigo F. Rezino - rodrigofrezino@gmail.com
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.
Neben Alexander, in der Regel wird dies als "Heapfragmentierung" genannt.
Beachten Sie, dass FastMM sollte elastische und insgesamt schneller sein, aber wenn die ursprüngliche App für den D7 memmanager abgestimmt wurde, FastMM könnte tatsächlich schlechter durchführen.
Nun, kann die Speichernutzung sogar erhöhen, wenn es kein in der Anwendung Speicherverlust ist. In diesen Fällen besteht die Möglichkeit, als Sie Leck einer anderen Ressource haben. Zum Beispiel, wenn Ihr Code zuweist, sagen, ein Bitmap und obwohl gibt er alle Objekte, sondern schafft über Finalisierung einige HBITMAP zu vergessen.
FastMM wird Ihnen sagen, dass Sie keine Speicherleck in Ihrer Anwendung haben, da Sie alle Ihre Objekte und Daten befreit haben. Aber Sie leckt noch andere Arten von Ressourcen (in meinem Beispiel - GDI-Objekte). Undichte andere Arten von Ressourcen, um Ihr Gedächtnis beeinflussen kann.
Ich schlage vor, Sie ein anderes Werkzeug, um zu versuchen, die nicht nur Speicherlecks überprüft, aber auch andere Arten von Lecks zu. Ich denke, dass AQTime Lage ist, dies zu tun, aber ich bin nicht sicher.
Ein weiterer möglicher Grund für dieses Verhalten ist die Fragmentierung des Speichers. Angenommen Mave Sie 2000 Objekte von 1 Mb in der Größe zugewiesen (lassen Sie sich für eine Minute über MM Gemeinkosten und das Vorhandensein von anderen Objekten im User-Space vergessen). Jetzt haben Sie die volle 2 GB Speicher beschäftigt. Angenommen nun, dass Sie frei alle auch Objekte, so dass Sie nun „gestrippt“ Speicherplatz haben, wobei 1 Mb beschäftigt und freie Blöcke gemischt werden. Obwohl jetzt haben Sie tun 1 GB freien Speicher, aber Sie sind nicht in der Lage einen Speicher für jedes 2MB-Objekt zuzuordnen, da die maximale Größe des freien Blockes 1 Mb ist nur (aber Sie haben 1000 solche Blöcke haben;)). Wenn der Speicher-Manager Blöcke verwendet größer als 1 MB für Ihre Objekte, dann kann es nicht die Speicherblöcke zurück in OS loslassen, wenn Sie Ihre selbst Objekte befreit haben:
[ [busy] [free] [busy] [free] [busy] [free] ]
[ [busy] [free] [busy] [free] [busy] [free] ]...
Diese großen [...] Blöcke sind halb voll, so MM sie nicht zu O geben kann. Wenn Sie einen anderen Block fragen werden, die> 1 Mb dann MM benötigen noch einen weiteren Block von OS zuzuweisen:
[ [busy] [free] [busy] [free] [busy] [free] ]
[ [busy] [free] [busy] [free] [busy] [free] ]...
[ [your-new-object] [free.................] ]
Hinweis , dass dies sind nur Beispiele für incresing Speichernutzung, wenn Sie nicht Speicherverlust haben. Ich sage nicht, dass Sie die genaue Situation haben: D