[Delphi:88994] レコード型のコンストラクタでメモリリーク

584 views
Skip to first unread message

Takuo Nakamura

unread,
Nov 26, 2006, 4:10:29 AM11/26/06
to del...@ml.users.gr.jp
中村です。

QCにレポート済みですが、Delphi 2006 の Win32用 Delphi で
レコード型のコンストラクタを使うとメモリリークすることがあります。

QC にレポートした例です。

1) Create Unit as below

unit VectorU;

interface

uses
SysUtils;

type
TVectorElements = array of Extended;

TVector = record
private
Fe: TVectorElements;
public
constructor Create(Size: Integer);
end;

implementation

{ TVector }

constructor TVector.Create(Size: Integer);
begin
SetLength(Fe, Size);
end;

end.

2) Call TVector's Constructors as below

procedure Test;
var
a: TVector;
begin
a := TVector.Create(1000000);
// Compiler generate code to initialize variable a (Initialize(a)) before
// Excecuting the body of Constructor. This behavior
// throws away memory allocated for a.Fe
a := TVector.Create(1000000);
end;

Call Test causes 10MB memory leak!!

回避方法ですが、
a := TVector.Create(1000000);
Finalize(a);
a := TVector.Create(1000000);

というように、コンストラクトする対象をあらかじめ Finalizeしておけば
大丈夫です。

補足:

CPUウィンドウで追ってみると、

a := TVector.Create(1000000);

の行では、まず Initialize(a) が呼ばれて動的配列のフィールドがクリアされ、
それから a.Fe に対して SetLength(Fe, Size); が実行されます。
従って、元々 a.Fe に入っていた動的配列が破棄されずに新しいメモリが
割り当てられてしまいます。

Delphi では戻り値は参照渡しのパラメータとして扱われるので、
a := TVector.Create(1000000); は a.Create(1000000); と同等になるようです。
a.Create(1000000); と書き換えてもメモリリークは発生します。

構文の形式上、従来どおり、aが抱えているメモリ(文字列、インターフェース、動的配列等)を
Finalize で破棄してから内容を変更するか、あるいは、CopyRecord(Delphiの内部関数)でaを
変更するべきなので、コンパイラのバグと思われます。

レコード型のコンストラクタが導入されたことで、構造体の初期化/再初期化をコンストラクタを使って
やることが当たり前になると思いますが、まだこんな落とし穴があるのでご注意を!

-------------------------------------------------------------------
中村拓男 東京都日野市
中村の里 http://www.asahi-net.or.jp/~HA3T-NKMR/ 2002/8/15
DGSサポート http://www.asahi-net.or.jp/~HA3T-NKMR/DGS/ 2002/9/26
PGP FP: AF 31 35 65 65 DB C1 8A AF F8 EC 08 CE 49 FF BA

Reply all
Reply to author
Forward
0 new messages