3

Windows Server 2008(x64、sp1)で実行すると、メモリフットプリントが着実に増加するD2007アプリケーションがあります。
Windows Server 2003(x32またはx64)、XPなどで正常に動作し、期待どおりに上下します。
付属のメモリマネージャまたは最新のFastMM44.92で試してみたところ、同じ結果が得られました。

Win2008でDelphiアプリのメモリ使用量を監視しようとした人はいますか?
または何か手がかりがありますか?

精度:
-常識的なメモリリークはありません(もちろん、私はFastMMなどに精通しています)
-メモリ使用量はProcessExplorerで監視されました。Win2008では、仮想メモリ(Private Bytes)と物理メモリ(WorkingSet Private)の両方が増加しています。メモリの
負荷がかかっていても、メモリ消費量は増加し続けていました。(それが失敗を引き起こしたので、私たちが調査するようになった方法ですが、Win2008ボックスでのみ)

更新://**リペース**//コードはアプリよりもはるかに単純ですが、同じ動作を示します。
10,000,000個のオブジェクトと10,000,000個のインターフェイスのリストを作成し、2回実行すると、使用メモリが最大60 MB増加し、Windows Server 2008でさらに100回実行すると、約300 MB増加しますが、XPの場合の状態に戻ります。
複数のインスタンスを起動した場合、他のインスタンスを実行できるようにメモリが解放されません。代わりに、ページファイルが大きくなり、サーバーがクロールします...

アップデート2QCレポート73347
を参照してください。 さらに調査した後、以下のコードに示すように、クリティカルセクションまで追跡しました。
そのコードをボタン付きの単純なVCLアプリケーションに配置します。そして、ProcessExplorerで監視し
ます。開始は約2.6MBで、5回の実行(ボタンをクリック)後も約118.6MBのままです。
5回の実行で116MBが失われました。

//***********************
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;
4

8 に答える 8

3

実際、マイクロソフトはクリティカル セクションに変更を加え、デバッグ情報を追加しました。このデバッグ メモリは、アプリケーションが終了するまで解放されませんが、何らかの方法でキャッシュされて再利用されるため、しばらくすると頭打ちになることがあります。

このメモリ ペナルティを感じずに多数のクリティカル セクションを作成したい場合の解決策は、VCL コードにパッチを適用して への呼び出しを への呼び出しに置き換えInitializeCriticalSectionInitializeCriticalSectionExフラグを渡してCRITICAL_SECTION_NO_DEBUG_INFOデバッグ構造の作成を回避することです。

于 2011-08-05T21:32:23.403 に答える
3

割り当てられたメモリを視覚化するVMMapと呼ばれる新しい sysinternals ツールがあります。たぶん、ビッグメモリブロックが何であるかを示すことができます.

于 2009-04-23T17:48:00.627 に答える
1

この問題があるかどうかを確認してください(これは別の問題であり、質問へのコメントで言及したものとは関係ありません)。

于 2009-04-25T07:33:57.233 に答える
1

アプリケーションでこの問題を修正するために、このコードを実行しました。FastCode の場合と同じです。修正を実行するには、ユニットをプロジェクトの最初のユニットとして配置する必要があります。この場合の uRedirectionamentos のように: ここに画像の説明を入力

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.
于 2011-08-04T22:28:04.743 に答える
1

Private Bytes、Virtual Size、または Working Set について言及していますか? SysInternals から Process Explorer を実行してメモリを監視し、何が起こっているのかをよりよく把握します。

私はこれについて具体的な経験はありませんが (2008 x64 SP1 を実行しているのでテストできます)、大量のメモリを割り当ててから解放するテスト アプリケーションを作成することをお勧めします。SysInternals から Process Explorer を実行して、メモリを監視します。

アプリケーションが同じ動作を再現することをテストする場合は、別のプロセスにメモリを割り当てて、メモリ プレッシャを作成してみてください。これは、最初のプロセスで以前に解放されたメモリが回収されない限り失敗するほどです。

それでも失敗する場合は、別のメモリ マネージャーを試してください。たぶん、それを行っているのは FastMM です。

于 2009-04-23T19:08:14.927 に答える
1

完全なデバッグ モードで FastMM を含めましたか? FastMM4 ユニットをプロジェクトに直接含めて設定するだけです

ReportMemoryLeaksOnShutdown := True

何も報告されていない場合は、通常、プログラムの終了時にすべてが解放されている可能性があります (参照カウントが原因である可能性があります)。AQTimeを使用して、メモリをリアルタイムで監視できます。このアプリケーションを使用すると、各クラス名と残りの使用済みメモリのバイト数が「カウント」されていることがわかります。誰がメモリを使用しているかがわかります。この仕事には期間限定のデモ版で十分です。

于 2009-04-23T08:57:50.150 に答える
0

アレクサンダーに加えて、通常これは「ヒープの断片化」と呼ばれます。

FastMM は全体的に回復力が高く、高速であると想定されていますが、元のアプリが D7 memanager 用に調整されている場合、FastMM のパフォーマンスは実際には低下する可能性があることに注意してください。

于 2009-04-25T22:35:51.840 に答える