The Delphi Bug List

Entry No.
408
VCL - 一般 - フォーム
Forms ユニットを使用する DLL の動的ロードを行うたびに、4KB(またはそれ以上)のメモリが失われる
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
Unknown Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Unknown Unknown Unknown N/A
解説
Reported by Martin Djernaes; checked by Erik Sperling Johansen
この問題は簡単に再現可能です:
  • 新規 DLL を作成します。
  • uses 節に Forms を追加します(それ以外は削除しても構いません)。
  • コンパイルします。
  • 新規アプリケーションを作成します。
  • 作成した DLL を、LoadLibrary を使ってロードします。
  • FreeLibrary を使ってその DLL を開放します。
  • (PView でメモリ使用量を監視しながら)これを3回繰り返してください。常に 4K のメモリが失われています。
    (訳注:タスクマネージャのプロセスリストで確認できます。PView は Microsoft Visual Stdio に付属するツールのようです)

原因:
問題は MakeObjectInstance で起こっています(というよりも、これが原因です)。
MakeObjectInstance の以下の行、

    Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ここで 4K ブロックのメモリがアロケートされていますが、これは開放されていません。
解決策 / 回避方法
以下の関数により、このメモリを開放することが出来ます(forms.pas の implementation セクションに書かないといけません)。
Procedure DeleteInstBlockList;
Var
  Block : PInstanceBlock;
Begin
  If InstBlockList <> nil
  Then Repeat
    Block := InstBlockList^.Next;
    VirtualFree(InstBlockList, 4096, MEM_DECOMMIT);
    InstBlockList := Block;
  Until Block = NIL;
end;
そして、forms.pas の finalization セクションから、この関数を呼び出してください。
finalization
  ..
  ..
  DeleteInstBlockList;

end.


Jean-Paul Collard 追記:
MemProof によれば、"entry0408.html"で説明されているメモリ損失(4KB)は、あらゆるアプリケーションに影響します(DLL だけではありません)。提案されている回避方法(Forms.pas の Procedure DeleteInstBlockList)は、私の環境ではハングアップします。

(Windows 98 SE + Delphi 3.0 で)正常に動作する解決方法を提案します:

SysUtils.pas の interface の任意の位置に変数を追加します:

var ErraticBlock: pointer;  // 追加

SysUtils.pas の finalization の最後に1行追加します:
finalization
  FreeTerminateProcs;
  DoneExceptions;
  VirtualFree( ErraticBlock, 4096, MEM_DECOMMIT);  // 追加
end.

最後に、Forms.pas に1行を追加します:

function MakeObjectInstance(Method: TWndMethod): Pointer;
const
  ......
begin
  if InstFreeList = nil then
  begin
    Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    ErraticBlock:=Block;  // 追加
    Block^.Next := InstBlockList;
    ......
ユーザーからのコメント
BigBro
30 Aug 2001  01:09 AM GMT
残念なことに、FFMT.OBJ が利用できないために SysUtils.pas を再コンパイル出来ません。
(エントリー 221 参照 - http://buglist.jrsoftware.org/generated/entry0221.htm)

Forms ユニットの finalization に書いてはだめなんでしょうか?
BigBro
30 Aug 2001  01:19 AM GMT
Stefan Hoffmeister によるもう1つの回避方法(http://www.automatedqa.com/support/leaksd5.asp)

この新しい手続きは、FORMS.PAS の InstBlockList の宣言より後ろに追加するべきです。

{$DEFINE FixVirtualAllocLeak}


{$IFDEF FixVirtualAllocLeak}


procedure  DeleteInstBlockList;
var  Block : PInstanceBlock;
begin
  Block := InstBlockList;
  while  Block <> nil do
  begin
    Block := InstBlockList^.Next;
    if VirtualFree(InstBlockList, 4096, MEM_DECOMMIT) then
      VirtualFree(InstBlockList, 0, MEM_RELEASE);
    InstBlockList := Block;
  end;
end;

{$ENDIF FixVirtualAllocLeak}

TApplication.Destroy(FORMS.PAS) の最後に1行追加します:

destructor TApplication.Destroy;
begin
  ..
  FIcon.Free;  
  {$IFDEF FixVirtualAllocLeak}
  DeleteInstBlockList;
  {$ENDIF FixVirtualAllocLeak}
end;
egoDust
02 Mar 2002  07:14 PM GMT
このバグは Delphi 6.0 にも存在します。上記の解決方法は正しく動作するものの、Classes.pas に移動する必要がありました。Forms.pas に書くバージョンは以下のようになっていました。
省略


結果として、
DeleteInstBlockList
の呼び出しは、デストラクタ
Application.Destroy


に置くことは出来ませんでしたが、Classes.pas の finalization 節に置くことは出来ました。
Michael in der Wiesche
18 Feb 2004  10:57 PM GMT
<http://www.thedelphimagazine.com/samples/1328/1328.htm> を見てください。別の解決策です。
Latest update of this entry: 2000-09-26
本家 The Delphi Bug List のエントリーはこちら
The Delphi Bug List 日本語訳 へ