3

FastMM は、次の行をメモリ リークの原因として報告します。

StrClassName := MidStr (curLine, length(START_OF_CLASSNAME)+1, length(curline)+1)

と はどうしたCopyMidStr?これは Delphi 2007 コンパイラのバグだけですか、それともそれ以降のバージョンにもこの問題はありますか? ここに FastMM レポートのコピーへのリンクと、私のアプリケーションがこれらの種類のレポートを表示する方法の画像があります。ノードを表示するVirtualTreeViewには、新しいデータ型が必要です。私はそれを TMemoryLeak と呼んでいます。レポートを解析するときにTMemoryLeak、クラス名、コールスタック、サイズなどを指定します。しかし、アプリがシャットダウンして FastMM が起動すると、上記のコピー行でメモリ リークが発生するようです。コールスタックのサイズ、オブジェクト全体の割り当てを解除しますが、文字列である ClassName フィールドは常にメモリをリークします。

更新(コメントから)

ここに宣言とコンストラクターとデコンストラクターがあります。寿命に関しては、オブジェクトがノードツリーを表示するために使用されるとすぐに、オブジェクトのデコンストラクターが呼び出されます。その後、それらは廃止され、割り当てが解除されます (希望します)。

TMemoryLeak = class(TObject)
    private
      fID              :integer;
      fSize            :integer;
      fTotalSize       :integer;
      fCallStack       :TStringList;
      fClassName       :string;
      fRepeatedInstance:integer;


    public
      property ID               :integer      read fID                write fID;
      property TotalSize        :Integer      read fTotalSize         write fTotalSize;
      property Size             :integer      read fSize              write fSize;
      property CallStack        :TStringList  read fCallStack         write fCallStack;
      property ClassName        :string       read fClassName         write fClassName;
      property RepeatedInstance :integer      read fRepeatedInstance  write fRepeatedInstance;
      class function Equal(xA: TMemoryLeak; xB: TMemoryLeak): Boolean;
      procedure clear;
      constructor create;
      destructor destroy; override;
  end;

  TMemoryLeakList=class(TObjectList)
    private
      fSortType         :TMlSortType;
      fSortDirection    :TMLSortDirection;
      fTotalLeakSize    :integer;
      fClassName        :string;
      fRepeatedInstance :Integer;
      fID               :Integer;
      function  GetItem(Index: Integer): TMemoryLeak;
      procedure SetItem(Index: Integer; const Value: TMemoryLeak);

    public
      property Items[Index: Integer]:TMemoryLeak      read GetItem             write SetItem; default;
      property TotalLeakSize        :integer          read fTotalLeakSize      write fTotalLeakSize;
      property SortType             :TMLSortType      read fSortType           write fSortType;
      property SortDirection        :TMLSortDirection read fSortDirection      write fSortDirection;
      property ClassName            :string           read fClassName          write fClassName;
      property RepeatedInstance     :integer          read fRepeatedInstance   write fRepeatedInstance;
      property ID                   :Integer          read fID                 write fID;

      function Add(AObject: TMemoryLeak): Integer;
      procedure Clear();
      procedure Sort;

      constructor create;
      destructor destroy; override;
  end;


constructor TMemoryLeak.create;
  begin
    inherited;
    fCallStack := TStringList.create;
    fRepeatedInstance:=0;
  end;
  destructor TMemoryLeak.destroy;
  begin
    clear;
  end;
  procedure TMemoryLeak.clear;
  begin
    fCallStack.Clear;
  end;
  class function TMemoryLeak.Equal(xA, xB: TMemoryLeak): Boolean;
  var i:Integer;
  begin
    Result:=False;

    if xA.ClassName = xb.ClassName then
    begin
      if xA.size = xb.size then
      begin
        if xA.CallStack.Count = xB.CallStack.Count then
        begin
          for i := 0 to xa.CallStack.Count - 1 do
          begin
            if CompareStr(xA.CallStack[i], xB.CallStack[i]) <> 0 then
            begin
             break;
            end;
          end;
          if i = xa.CallStack.Count then
            Result:=True;
        end
      end
    end

  end;

  { TMemoryLeakList }

  constructor TMemoryLeakList.create;
  begin
    inherited;
    fSortType         :=stID;
    fSortDirection    :=sdAsc;
    fClassName        :='';
    fRepeatedInstance :=0;
  end;
  destructor TMemoryLeakList.destroy;
  begin
    Clear;
  end;
  procedure TMemoryLeakList.Clear;
  var i : Integer;
  begin
      for i := 0 to Count - 1 do
        Items[i].clear;
  end;
4

1 に答える 1

11

理にかなっている説明は、メモリリークがあるということです。

FastMMリークレポートがどのように機能するかについて誤解していると思います。Copyリークレポートから、などがリークの原因であると推測しているようですMidStr。そうではありません。メモリが割り当てられたが、その後割り当てが解除されなかった場合にリークが報告されます。Copyやのような関数の場合MidStr、それらの仕事は、メモリの割り当てを自然に伴う新しい文字列を作成することです。文字列バッファを保持するために作成されたメモリの割り当てが解除されなかったため、リークが報告されます。コードの残りの部分がそのメモリの割り当てを解除できない場合、それは割り当て関数Copyの障害ではありません。MidStr

Delphi 2007は成熟した製品であり、文字列のメモリ管理は正しいことが知られています。おそらく、文字列の参照カウントをバイパスする手動のメモリコピーを実行します。nilを呼び出して、いくつかの変数/フィールドをに設定していますFillCharか?FreeMemの代わりにレコードを破棄していますDisposeか?前者は、文字列の参照カウントをデクリメントしません。このようなものがリークの原因である可能性があります。


あなたが投稿したコードの抜粋を見ると、これは問題です:

destructor TMemoryLeakList.destroy;
begin
  Clear;
end;

継承されたデストラクタの呼び出しに失敗しました。これは、リストのメンバーが破棄されないことを意味します。これは、文字列が破棄されない理由を説明しています。

実際、リストクラスにデストラクタを提供する必要はありません。単にそれを削除し、継承されたTObjectListデストラクタに作業を任せます。OwnsObjectsデフォルトは、であるためTrue、リストのメンバーは、リストから削除されるとすぐに、またリスト自体が破棄されるとすぐに破棄されます。

あなたのClearメソッドが実際にリストをクリアした場合、それは起こります。しかし、あなたClearは本物ではありませんClear。コンテナ内の実数Clearは、すべてのメンバーを削除することが期待されています。を削除Clearし、継承されたバージョンに依存する必要があります。

ではTMemoryLeak、継承されたデストラクタの呼び出しにも失敗します。また、そのクラスが所有する文字列リストインスタンスの破棄にも失敗します。

要約すると、これらのコンストラクタとデストラクタは次のように記述します。

constructor TMemoryLeak.Create;
begin
  inherited;
  fCallStack := TStringList.Create;
end;

destructor TMemoryLeak.Destroy;
begin
  fCallStack.Free;
  inherited;
end;

constructor TMemoryLeakList.Create;
begin
  inherited;//by default OwnsObjects is set to True, list manages member lifetime
  fSortType :=stID;
  fSortDirection :=sdAsc;
end;

次にdestructor、を削除し、Clearメソッドを削除します。継承されたバージョンでTObjectList十分です。

あなたが述べるコメントの中で:

オブジェクトがノードツリーの表示に使用されるとすぐに、オブジェクトのデストラクタが呼び出されます。その後、それらは廃止され、割り当てが解除されます(私は願っています)。

これが役に立たない可能性は十分にあると思います。モードでオブジェクトリストを作成したのでOwnsObjects、リストのメンバーをまったく破棄しないでください。あなたはリスト自体にそれをするように頼んだ。両方を行うことはできません。そして、「私は願っています」というコメントは、このコードが正しいという自信を私に与えてくれません。

すべてのコードを確認することはできないため、これが問題のすべてであるとは思えません。

肝心なのは、コードにリークがあるということです。FastMMを信頼してください!

于 2012-11-14T13:54:14.403 に答える