メモリリークが無いのにOSのメモリが減っていく

解決


take  2006-12-16 10:57:39  No: 24267  IP: 192.*.*.*

Delphiで作ったアプリケーションの試験をしているのですが
メモリリークは無いのにOSのメモリが消費されていきます。
常駐ソフトは全て外してウイルスチェックスパイウエアチェック済みです。

メモリリークが無いという根拠はMemcheckの他、タスクマネージャーで
プロセスを見るとDelphiアプリが使用しているメモリが増えていないからです。
ですが、コミットチャージの欄のメモリは増えていき半日で50Mほど増えます。
消費されたメモリはDelphiアプリを終了しても解放せず、再起動まで残ります。もちろんこのアプリを起動しなければ発生しない現象です。

1クラス毎に色々調べてはいるのですがDelphiアプリのメモリ消費量の
表示には問題がないせいもあり原因がつかめません。

そもそもDelphiが使用していないのにメモリが減るようなことがあるので
しょうか?

編集 削除
かみづ  2006-12-16 14:35:08  No: 24268  IP: 192.*.*.*

例えば共有メモリのようにシステムワイドなリソースを消費している場合は
ありえるんじゃないでしょうか。

編集 削除
take  2006-12-16 15:35:11  No: 24269  IP: 192.*.*.*

レスありがとうございます。
基本的なDelphiアプリの内容としては、ログを取っていくだけのもので
共有メモリは使っていません。

試しにメモリクリーナーを使ってみましたが変化無しでした。
ファイルかなにかのキャッシュに使われているのかと思いましたが
どんなに空きメモリが減っても開放することは無くOSからメモリ不足の
警告が出ます。

編集 削除
かず  2006-12-16 15:50:00  No: 24270  IP: 192.*.*.*

他のアプリでも同じように消費されていくのではないでしょうか。
Delphiとは無関係のような気がしますが。
たとえばExcelを起動して、終わっても元の空きメモリ容量にならない。

編集 削除
take  2006-12-16 16:31:27  No: 24271  IP: 192.*.*.*

たしかにIEとかを起動し終了しても空きメモリの量が戻らない現象は
あります。
IEとかでは10M消費していたとしたら、終了させると8Mぐらいは
返ってくるのですが、

今回作成したDelphiアプリを起動すると実行ファイルが6M。
データ保存用クラスの生成で10Mの計16Mになったとすると
終了させても6Mしか開放されず、まるでメモリリークのようになって
いるのです。

何度実行、終了してもメモリの消費ばかりが続き、OSを再起動しないと
開放されません。

編集 削除
HOta  2006-12-16 16:43:50  No: 24272  IP: 192.*.*.*

テストで、データ保存用クラスを生成しなければ、6Mだけで問題にならないのでしょうか?少しずつ外したりしてテストするしか無いのかな?

編集 削除
take  2006-12-16 17:04:34  No: 24273  IP: 192.*.*.*

保存クラスに問題があると思うので、半分ぐらいの処理をコメント化した
のですが、やはり発生しました。

HOtaさんがおっしゃるとおり思い切って全部コメントにして少しずつ
テストしたいと思います。

起動してから現象を確認するまで20〜30分かかるため、もう少し
現象を出やすくするテストもしてみます。

編集 削除
かず  2006-12-16 21:40:38  No: 24274  IP: 192.*.*.*

私は仕事、趣味でDelphiを使用していますが、このような現象は一度もありません。
プログラミング上の問題のような気がします。どこかに悪い部分が存在するのでしょうね。
具体的な処理内容やコードがわからないと答えられないかもしれませんね。

編集 削除
deldel  2006-12-18 09:08:44  No: 24275  IP: 192.*.*.*

以下を参照下さい。
http://leed.issp.u-tokyo.ac.jp/~takeuchi/delphi/article/080/080495.html

で、ここのFastMMを入れると解決するかもです。
http://sourceforge.net/projects/fastmm/

編集 削除
かみづ  2006-12-18 21:02:53  No: 24276  IP: 192.*.*.*

>以下を参照下さい。
>http://leed.issp.u-tokyo.ac.jp/~takeuchi/delphi/article/080/080495.html

この現象の場合、アプリを終了したらメモリは回復すると思うのですが・・・。

編集 削除
take  2006-12-19 20:23:01  No: 24277  IP: 192.*.*.*

色々レスありがとうございます。
問題部分をコメントにするほか各環境にて確認したところなんと非力な環境でのみ発生するようです。
もしかすると
http://ahfb1.kek.jp/~tobiyama/epics/epics-pc/troubles.html#Q2
の現象かもしれません。

データ保存処理に時間がかかりすぎて、この処理中に再実行され解放されないまま終了していくのかもしれません。

まだ推測ですので証明まで少々お待ちください。

編集 削除
助平  2006-12-20 10:30:25  No: 24278  IP: 192.*.*.*

例)

procedure THogeForm.Button1Click(Sender: TObject);
var
  button:TButton;
begin
  button:=TButton.Create(Self);
  button.Parent := Self;
  〜〜
  //最後のbutton.Free; を忘れる
end;

ボタンを押す毎に、新しいボタンインスタンスを作成するが、破棄しないためメモリ・リソースを食いつぶす。
ただし、ボタンの所有者がフォームのため、フォームの破棄時に、
一緒に破棄されるため、メモリーリークとして報告されない。

このように、誰かが代わりにお掃除してくれる場合、見落としてしまう場合があります。

編集 削除
take  2006-12-26 09:33:20  No: 24279  IP: 192.*.*.*

原因となる箇所が突きとめられました。
しかし、環境により発生したりしなかったりとまだまだ謎です。
ソースを示しますが問題部分を大げさに抽出しただけで
さらにこれを実行しても現象は発生しませんでした。

下記のようなコードでSaveToFileを呼ぶと現象が発生しメモリが
減っていくようです。
※Delphiアプリの消費メモリは変動なし、終了してもメモリは回復しない

【概要】
いくつかのTStringListをTListによって管理させます。
ただし生成時、独自拡張したTStringListExを生成させ開放はTStringListとして
実行します。(プログラムミスを再現)
値を入れておいたTStringListExから取り出した値はまとめてファイル化します。

【現象発生の可否】
非力なWindows2000SP3環境で発生。
TPerson.SaveToFileメソッド中のSaveToFileをコメント化すると改善
プログラムミスである 
  TStringList(t[j]).Free;

  TStringListEx(t[j]).Free;
にすると改善

以下がソースです。
※TPersonの生成やフォームの定義は省略

// TStringListの拡張クラス
type
  TStringListEx = class(TStringList)
  private
    { Private 宣言 }
    FP : Pointer;
  public
    { Public 宣言 }
    constructor Create();
    destructor Destroy();override;
  end;

type
  TPerson = class(TPersistent)
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    function SaveToFile(const FileName : string) : Boolean;
  end;


{ TStringListEx }

constructor TStringListEx.Create();
begin
  // 状態を明確にするためメモリを大量確保
  FP := AllocMem(1000000);
end;

destructor TStringListEx.Destroy;
begin
  FreeMem(FP);
  inherited;
end;

{ TPerson }

function TPerson.SaveToFile(const FileName: string): Boolean;
var
  t : TList;
  ts,tt : TStringList;
  i,j : Integer;
begin
  t := TList.Create;
  tt := TStringList.Create;
  try
    // TStringListExを生成しリストに追加
    for j := 0 to 25 do begin
      ts := TStringListEx.Create;
      ts.Add(StringOfChar(Char($41 + j),100));
      t.Add(ts);
    end;

    // リストから保存用のTStringListを作成し保存
    for j := 0 to t.Count-1 do begin
      ts := TStringList(t[j]);
      tt.Add(ts.Text);
    end;
    tt.SaveToFile(FileName);

    // 不要なクラスを破棄
    for j := 0 to t.Count-1 do begin
      TStringList(t[j]).Free;         // ←問題点
    end;
    t.Clear;

  finally
    tt.Free;
    t.Free;
  end;
end;

編集 削除
o...fZ  2006-12-26 10:15:10  No: 24280  IP: 192.*.*.*

> TStringList(t[j]).Free;         // ←問題点
  ↓
  ts := TStringList(t[j]);
  ts.Free;

かなぁ・・・

>   ts,tt : TStringList;
これも、TStringsで宣言するかも・・・気分で

または、D5以降であれば、TListではなく、contnrsをuses して、TObjectListを使って、Clearするだけにする。

編集 削除
かみづ  2006-12-26 23:22:05  No: 24281  IP: 192.*.*.*

デストラクタは
   destructor Destroy();override;
のように仮想なので
  TStringList(t[j]).Free;
は問題ないと思うのですが・・・

編集 削除
orz  2006-12-27 09:03:18  No: 24282  IP: 192.*.*.*

TStringList(t[j]).Free;
これで、TStringListEx.Destroy; は実行されるし、
ttp://www.yks.ne.jp/~hori/MemCheck.html によりメモリーリークチェックしても報告されない。

では・・・これが当たりか?
ttp://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=080495

編集 削除
take  2006-12-27 09:12:05  No: 24283  IP: 192.*.*.*

色々ご教授ありがとうございます
サンプルで言うところのTStringListEx.Destroyメソッドが
ちゃんと実行されメモリリークなど発生しない事は
わかるのですが、現象がこれで改善されてしまいます。
いくつかの環境で試しているのですが

A:Celeron400MHz MEM:128M OS:Win2000(Pro)SP4 状態:×
B:Celeron400MHz MEM:128M OS:Win2000(Server)SP4 状態:○
C:PentiumM1.5M MEM:768M OS:WinXP(HOME)SP1 状態:○

状態が×の環境が他に2例ほどあるのですがAの環境に酷似している
こと以外は不明です。

状態が×のものではタスクマネージャーのプロセス一覧とパフォーマンスの
メモリ表示で確認すると
プロセス一覧のメモリには変化が無く、パフォーマンスのメモリが減り
最後にはOSが不安定になります。

見えないプロセスがあるようなイメージなので、色々調べているのですが
まだ解決には至りません。

開発環境をDelphi5SP1からDelphi6にして変化があるか試す事にします。

編集 削除
take  2006-12-27 09:19:12  No: 24284  IP: 192.*.*.*

書き込み中に返信があったようで再度書き込みます。
orzさんのリンク先にあった「FastMM」または同等品を導入して
変化があるか試したいと思います。

編集 削除
take  2006-12-28 08:19:31  No: 24285  IP: 192.*.*.*

FastMMを使ったところ状況が改善されました。
年の瀬で時間がなかったので、FastMMを含めたいくつかを
同時に改善したため断定ではありませんが
原因の1つになっていると思われます。

この後、改善した箇所を1つずつ元に戻しながら試験を行いますが
時間がかかるため一旦解決と致します。

ありがとうございました。

編集 削除
deldel  2006-12-28 09:19:07  No: 24286  IP: 192.*.*.*

>FastMMを使ったところ状況が改善されました。
ここの上から9つ目のレスで書いていたのですけどねぇ・・・
気づかなかったのかな?(>_<;)

編集 削除
orz  2006-12-28 10:43:46  No: 24287  IP: 192.*.*.*

> >FastMMを使ったところ状況が改善されました。
> ここの上から9つ目のレスで書いていたのですけどねぇ・・・
あ〜、ほんとだねぇ。
しかも、MLの同じ記事指しているし、具体的にFastMMの名前まで出しておられる。
あたくしのレスより詳しいではありませんか(^^ゞ

次の、これでスルーしちゃったとか
> この現象の場合、アプリを終了したらメモリは回復すると思うのですが・・・。

コレに懲りずにがんばっていきましょう。

編集 削除
take  2006-12-28 15:01:08  No: 24288  IP: 192.*.*.*

すみません
本当にその通りでリンク先も読んでおきながら次のレスで
リンク先の現象=終了で回復
と思いこんでしまっていました。
せっかく的確なアドバイスを頂いたdeldelさんには申し訳ないです。

編集 削除