12

FastMM4 によると、現在取り組んでいる Delphi プログラムは多くの文字列をリークしています。正確には AnsiStrings:

ここに画像の説明を入力

アプリケーション ( http://sourceforge.net/projects/orwelldevcpp/ ) は、他の多くのデータ型をリークしていましたが、FastMM4 はインスタンスが作成された場所を報告できたので、なんとか修正しました。奇妙なことに、FastMM4 はこれらのリークの場所をまったく報告しません。

編集:結局のところそうであるようです。修正の回答を参照してください。とにかく、問題はまだ残っています: どうして私はこれらのものを漏らしているのですか?

ええと、残念ながら、何を探すべきかわかりません。つまり、これらが範囲外になった場合、それらは自動的に正しく解放されるはずです (ヒープ上にある場合でも)。

ランダムにコメントして、カウントに何が起こるかを確認することで、いくつかのリークを追跡することができました. 次に例を示します。

// simply passing it a constant creates a leak...
MainForm.UpdateSplash('Creating extra dialogs...');

procedure TMainForm.UpdateSplash(const text : AnsiString);
begin
  if not devData.NoSplashScreen then // even if this branch is NOT taken
    SplashForm.Statusbar.SimpleText := 'blablabla' + text;
end;

// And even if the function call itself is placed within a NOT taken branch!

リークの別の例を次に示します。

// Passing this constants produces leaks...
procedure TCodeInsList.AddItemByValues(const a, b, c: AnsiString;...);
var
  assembleditem : PCodeIns;
begin
   new(assembleditem);
   assembleditem^.Caption:=a;
   assembleditem^.Line:=b;
   assembleditem^.Desc:=c;
   ...
   fList.Add(assembleditem);
end;

// ... even when calling this on WM_DESTROY!
destructor TCodeInsList.Destroy;
var
  I: integer;
begin
  for I := 0 to fList.Count - 1 do
    Dispose(fList[I]);
  fList.Free;
  inherited Destroy;
end;

// produces leaks!?

ここにはかなりの数の文字列リークの質問がありますが、どのパターンを探すべきかを明確にするものはありません。Google も提供していません。

編集:だから、渡された定数を探す必要があります。しかし、なぜ?

ええと、何かアイデアはありますか?

4

5 に答える 5

14

文字列を明示的に割り当てる必要はありません。参照カウントによるマングリングとは別に、オブジェクトまたはレコードの文字列フィールドもリークする可能性があります。例えば、

type
  PRecord = ^TRecord;
  TRecord = record
    S: string;
  end;

procedure TForm1.Button4Click(Sender: TObject);
var
  r: PRecord;
begin
  GetMem(r, SizeOf(r^));
  Initialize(r^);
  r.S := ' ';
  FreeMem(r);

上記の例では、レコード自体のメモリが解放されるため、FastMM はリークされた文字列のみを報告します。


いずれにせよ、FastMM がダイアログにスタック トレースを表示しないからといって、その情報が不足しているわけではありません。FullDebugModeLogMemoryLeakDetailToFileおよびLogErrorsToFile「FastMM4Options.inc」で定義されていることを確認してください。次に、実行可能ファイルのディレクトリで「[ExecutableName]_MemoryManager_EventLog.txt」ファイルを探します。

上記の例では、FastMM は次のファイルを生成します。

-------------------------------- 2012/5/27 4:34:46-------------------- ------------------------
メモリ ブロックがリークしました。サイズは: 12

このブロックが割り当てられたときのスタック トレース (リターン アドレス):
40305E
404B5D
404AF0
45C47B
43D726
42B0C3
42B1C1
43D21E
76C4702C [GetWindowLongW]
77AE3CC3 [RtlImageNtHeader の未知の関数]

ブロックは現在、次のクラスのオブジェクトに使用されています: 不明

割当数:484

ポインタ アドレス 7EF8DEF8 から始まる 256 バイトの現在のメモリ ダンプ:
01 00 00 ...
...

これで、アプリケーションを実行して一時停止し、アドレスを検索できます。上記のログとテスト アプリケーションの場合、アドレスは次のように解決されます。

このブロックが割り当てられたときのスタック トレース (リターン アドレス):
40305E -> _GetMem
404B5D -> _NewAnsiString
404AF0 -> _LStrAsg
45C47B -> TForm1.Button4Click (FreeMem 行)
43D726 -> TControl.Click
...


編集: アドレスを手動で検索する代わりに、リンカ オプションを使用して詳細なマップ ファイルを生成すると、FastMM がそれを行います (Mason のコメントに感謝します)。


質問に対するあなたの編集は、上記の例のような非常によく似たリークを反映しています。「fList」が通常の である場合、TListポインターを保持するだけで、それらのポインターが何を指しているかはわかりません。したがって、ポインターを破棄すると、レコードのフィールドではなく、ポインター自体に割り当てられたメモリのみが解放されます。したがって、リークは関数に渡される定数とは関係ありませんが、次のパターンのようになります。

var
  assembleditem: PCodeIns;
  p: Pointer;
begin
  new(assembleditem);
  assembleditem^.Caption:='a';
  ..    
  p := assembleditem;
  Dispose(p);

レコードを破棄するには、コードでその型へのポインターを型キャストする必要があります。

Dispose(PCodeIns(p));

したがって、「TCodeInsList.Destroy」は次のようになります。

destructor TCodeInsList.Destroy;
var
  I: integer;
begin
  for I := 0 to fList.Count - 1 do
    Dispose(PCodeIns(fList[I]));
  fList.Free;
  inherited Destroy;
end;


最後に、探しているパターンは、コードが文字列フィールドを持つレコード (可能性が低いオブジェクト) を解放しようとしている場所を探しているようです。を探して、割り当てられたメモリがリークされたときに FastMM が示すオブジェクト/レコードのメモリを解放する可能Dispose性が少し低くFreeMem、さらに可能性が低いことが役立ちます。FreeInstance

于 2012-05-27T01:37:17.057 に答える
4

文字列を自動的にクリーンアップする必要があることは正しいです。ただし、それを台無しにするいくつかの方法を見てきました。

1 つ目は、参照カウントを壊す可能性のある文字列データ構造を直接操作している場合です。これは、リークしている文字列の数で最も可能性が高いです。

もう 1 つは、Halt を呼び出して、スタックに文字列参照を残すことです。しかし、スタックに 40,000 個の文字列参照を残すつもりはないので、文字列を渡されてその参照カウントをいじるコードを探します。

于 2012-05-26T17:03:46.410 に答える
1

短い単語では、Delphi 組み込みの文字列型は参照カウントされます。メモリ割り当ておよび破棄メソッドは、参照カウントの更新を処理しないため、コンパイラは、レコード内の文字列が実際に解放される可能性があることを知りません。

参照カウント文字列型でレコードを定義することはお勧めできません。以前も同じ混乱がありました。Delphi ライブラリのソースを調べてみると、多くのレコードには文字列ではなく PChar が含まれていることがわかります。

レコードについて話し合う人もいます

于 2012-05-28T16:50:10.360 に答える