Delphi 2009-インターフェイスプロパティによりメモリリークが発生する可能性はありますか?
-
10-07-2019 - |
質問
FastMM4によって報告されたメモリリークの2MBテキストファイルを持つIntrawebアプリを継承しました。 1つのクラスの115インスタンスで52バイトのリークが発生しています。
悪役の簡単な説明:
TCwcBasicAdapter = class(TCwcCustomAdapter)
protected
FNavTitleField: TField;
function GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav; override;
public
constructor Create(aDataSource: TDataSource; aKeyField, aNavTitleField: TField; aMultiple: boolean);
end;
そしてインターフェースは:
ICwcCDSAdapterNav = interface(IInterface)
プロパティは参照カウントされるため、間違ったツリーを呼び出していますか?インターフェイスプロパティがクラスの破壊を防ぐことができる状況はありますか?
上記のメソッドの実装は次のとおりです。
function TCwcBasicAdapter.GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav;
var
AdapterNav: TCwcCDSAdapterNavBase;
begin
result := nil;
if Assigned(aDataSet) then begin
AdapterNav := TCwcCDSAdapterNavBasic.Create(aDataSet, FKeyField.Index, FNavTitleField.Index);
try
AdapterNav.GetInterface(ICwcCDSAdapterNav, result);
except
FreeAndNil(AdapterNav);
raise;
end;
end;
end;
クラスが次のように宣言されている
TCwcCDSAdapterNavBase = class(TInterfacedObject, ICwcCDSAdapterNav)
解決
FastMMは、何がリークされ、どこで作成されたかを示します。
それは本当の犯人にそれを絞り込むのを助けるでしょう:誰が何を漏らしているのですか?
あなたの質問が本当に何なのか分かりませんか?
コードは不完全であるか、問題のコードではありません。クラスにはInterfaceプロパティもInterface private Fieldもありません。インターフェースを返すメソッドであり、無害です。
編集:ICwcCDSAdapterNavを実装しているオブジェクトのコードを見なければ、実際に参照カウントされているかどうかはわかりません。
TInterfacedObjectから派生していない場合、チャンスは参照カウントされない、この自動解放に依存できない ...
この CodeRage 2セッションをご覧ください:ダミーのメモリリークとの戦い。主に、DelphiでFastMMを使用してメモリリークを防止/検出する方法を示します。 D2007用でしたが、他のバージョンにも関連がありました。
他のヒント
FastMMの仕組みについては、これまでにいくつかの良い答えがあります。しかし、実際の質問については、はい、インターフェイスオブジェクトは2つの異なる方法でリークする可能性があります。
- インターフェイスは、所属するオブジェクトが_AddRefメソッドと_Releaseメソッドで参照カウントを実装している場合にのみ参照カウントされます。一部のオブジェクトはそうではありません。
- 循環インターフェイスの参照がある場合(インターフェイス1はインターフェイス2を参照し、インターフェイス1はインターフェイス1を参照します)、参照カウントは0にはなりません。これがあなたの問題であるならば、私はAndreas Hausladenのこのテーマに関する最近のブログ投稿を参照します。
そのクラスの115個のインスタンスをリークしている場合、リークされているのはそのクラスです。参照しているものが占有しているメモリではなく、そのクラスが占有しているメモリがリークされています。どこかに、解放していない TCwcBasicAdapter
のインスタンスが115個あります。
さらに、プロパティは、インターフェースや他のタイプであってもデータを保存しません。フィールドだけがメモリを占有します(コンパイラがクラスに代わって割り当てる隠しスペースもあります)。
それで、はい、あなたは間違ったツリーをbarえています。メモリリークは別の場所にあります。 FastMMがメモリリークがあることを通知するとき、リークした各インスタンスが割り当てられた場所も通知しません。その機能があります。いくつかの条件付きコンパイルシンボルを調整して、その機能を有効にする必要がある場合があります。
確かに、リークしているのはそのクラスのインスタンスだけではありません。 FastMMは、クラスのインスタンスやインターフェイスを実装するクラスなど、リークしている他のいくつかのことも報告する必要があります。
追加した関数に基づいて、リークしているのは本当に TCwcCDSAdapterNavBase
であると疑い始めました。 GetAdapterNav
の例外ハンドラーは実行されますか?疑わしい; TObject.GetInterface
は、明示的に例外を発生させることはありません。オブジェクトがインターフェイスをサポートしていない場合、 False
を返します。例外ハンドラーがキャッチできるのは、アクセス違反や違法な操作などです。これらを実際にキャッチするべきではありません。
次のように、その機能をより直接実装できます。
if Assigned(FDataSet) then
Result := TCwcCDSAdapterNavBase.Create(...);