[delphi-users:2373] メモリリークを防ぐ

1,292 views
Skip to first unread message

遊歩人

unread,
Dec 27, 2011, 10:23:34 PM12/27/11
to DelphiML

みなさん、こんにちは。

 

作成したクラスを確実に解放するために、tryfinally で括ると思いますが、

以下の場合、fooの中で例外が発生したら、インスタンス "List" が解放されません。

 

みなさんは、どの様にされていますか?

ご教授よろしくお願いします。

 

 

procedure TForm1.FormCreate(Sender: TObject);

var

  List: TStrings;

  i: Integer;

begin

  try

    List := TStringList.Create;

    List.LoadFromFile('DATA.txt');

 

    for i := 0 to List.Count - 1 do

    begin

      // 何らかの処理

      foo(List.Strings[i]);

    end;

 

  finally

    // finally節でメモリを確実に解放する

    List.Free;

  end;

end;

 

procedure TForm1.foo(Sender: String);

begin

  raise Exception.Create('何らかのエラー');

end;

 

 

遊歩人

ありい

unread,
Dec 27, 2011, 10:47:13 PM12/27/11
to delphi...@freeml.com
 遊歩人さん、こんにちは。

> 以下の場合、fooの中で例外が発生したら、インスタンス "List" が解放されませ
> ん。

 D7では正常にfinallyにやってきます。List.Free;の後に
ShowMessage('finally'); と入れると表示されます。

 遊歩人さんがfinallyにやってこないと判断された方法は
どのようなものでしょうか?

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hHvLK
------------------------------------------------------[freeml byGMO]--

遊歩人

unread,
Dec 28, 2011, 12:02:07 AM12/28/11
to delphi...@freeml.com
ありい さん、こんにちは。

早々のリプライありがとうございます。

昔の「DEV CAMP」の資料を見ていて、「インスタンス "List" が解放されません」
というメモが残っていました。

メモリリークには、try~finallyを入れて気を付けていたので質問させて頂きまし
た。

遊歩人

 遊歩人さん、こんにちは。

 遊歩人さんがfinallyにやってこないと判断された方法は
どのようなものでしょうか?

ありい


MLホームページ: http://www.freeml.com/delphi-users


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=hHwFB
------------------------------------------------------[freeml byGMO]--

ありい

unread,
Dec 28, 2011, 12:33:00 AM12/28/11
to delphi...@freeml.com
> 昔の「DEV CAMP」の資料を見ていて、「インスタンス "List" が解放されません」
> というメモが残っていました。
> メモリリークには、try~finallyを入れて気を付けていたので質問させて頂きまし
> た。

 そうですか...

 実際に試されて問題がなければ、大丈夫だと思います。

 というか私も同様の記述をしていますので、解放されて
ないと、かなり困ります(^^;

 1点だけ、遊歩人さんが書かれた↓のコードですが、

> try
> List := TStringList.Create;
> List.LoadFromFile('DATA.txt');

(以下略)

 以下のようにされることをお勧めします。

List := TStringList.Create; // tryの手前に引っ越す
try
List.LoadFromFile('DATA.txt');

 万が一、TStringList.Createが失敗して例外を返した時
に、遊歩人さんのコードでは生成されていないListでFree
が走る可能性があります。

#今時はあまりないとは思いますが(^^; 強いて思いつく
#ケースを挙げるとメモリ不足とか...?

 ちょっと気になりましたので書かせて頂きました m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hHwZb
------------------------------------------------------[freeml byGMO]--

高木太郎

unread,
Dec 28, 2011, 1:08:12 AM12/28/11
to delphi...@freeml.com
 こんにちは、イマジオムの高木です。

遊歩人さん、ありいさん:

procedure TForm1.FormCreate(Sender: TObject);
var
List: TStrings;
i: Integer;
begin

try
List := TStringList.Create;
List.LoadFromFile('DATA.txt');

for i := 0 to List.Count - 1 do
begin
// 何らかの処理
foo(List.Strings[i]);
end;
finally
// finally節でメモリを確実に解放する
List.Free;
end;
end;

>  以下のようにされることをお勧めします。


>
> List := TStringList.Create; // tryの手前に引っ越す
> try
> List.LoadFromFile('DATA.txt');
>
>  万が一、TStringList.Createが失敗して例外を返した時
> に、遊歩人さんのコードでは生成されていないListでFree
> が走る可能性があります。
>
> #今時はあまりないとは思いますが(^^; 強いて思いつく
> #ケースを挙げるとメモリ不足とか...?

 ありいさんのおっしゃるように、コンストラクタが失敗する
ケースもありますので、次のどちらかにすることをお勧めします。

List:=TStringList.Create;
try
<いろいろな処理>
finally
List.Free;
end;

List:=nil;
try
List:=TStringList.Create;
<いろいろな処理>
finally
List.Free;
end;

 ちなみに私は後者が好きです。 後者であれば(コンストラクタが
失敗する可能性のある)変数が複数になっても同じスタイルで
いけるからです。

List1:=nil;
List2:=nil;
try
List1:=TStringList.Create;
List2:=TStringList.Create;
<いろいろな処理>
finally
List1.Free;
List2.Free;
end;

 GetMem などのリソース確保も、同じように実装すると統一感があって
いいですね。

Ptr:=nil;
try
GetMem(Ptr,1024);
<いろいろな処理>
finally
FreeMem(Ptr);
end;


 余談ですが、上記のようなことを考えると、デストラクタは
「コンストラクタがどこでエラーになっても、問題を出さない
ように作らないといけない」ことがわかります。 ライブラリの
終了処理なども同様です。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
毎日豪華プレゼントキャンペーン開催中!くまポン
http://ad.freeml.com/cgi-bin/sa.cgi?id=hHxxN
------------------------------------------------------[freeml byGMO]--

ありい

unread,
Dec 28, 2011, 1:41:35 AM12/28/11
to delphi...@freeml.com
 高木さん、こんにちは。

> List1:=nil;
> List2:=nil;
> try
> List1:=TStringList.Create;
> List2:=TStringList.Create;
> <いろいろな処理>
> finally
> List1.Free;
> List2.Free;
> end;

 な・る・ほ・ど~☆

 高木さんのソースを見て、やっっっとヘルプ(D7)のFreeの
言ってる意味が理解できました(^^;

(D7ヘルプより引用)
> オブジェクトが nil の場合でも Free メソッドはエラーにはなりません。
> このため,オブジェクトが初期化されていない場合に Free メソッドを呼び出してもエラーにはなりません。

 List2のCreateで失敗しても確実にList1を破棄して、List2
はスルーしてくれるんですね!

 nilのFreeが呼べることを知ってはいたのですが、感覚的に
とても気持ち悪くて今まで使用を避けていたのですが、おかげ
さまで大分すっきりしました。

#nil.Freeって思うと、やっぱり完全には払拭できませんが(^^;
#一種のシンタックスシュガーと思い込もうと思えば思えなく
#もないというか...(^^;

 ...考え中...

 ...今後、高木派に改宗しますっ!

 大変勉強になりました。ありがとうございました m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hHxJT
------------------------------------------------------[freeml byGMO]--

hosokawa

unread,
Dec 28, 2011, 1:54:51 AM12/28/11
to delphi...@freeml.com
横から失礼します。

細川です。

> #nil.Freeって思うと、やっぱり完全には払拭できませんが(^^;
> #一種のシンタックスシュガーと思い込もうと思えば思えなく
> #もないというか...(^^;

nil.Free っていう訳では無いのです。
TMethod ヘルプとかをみると判りますがメソッドポインタというのは2つに別れ
ています。
コードへのポインタと、インスタンスごとのデータへのポインタです。
TMethod でいうと

TMethod.Code
TMethod.Data

です。
それぞれポインタになっています。

Free のコードを見ると Self が nil かどうかを調べています。
これはインスタンスごとのデータが nil なのかどうか、ということで、コード
自体は存在しているのです。

よろしくお願いします。


Regards,
HOSOKAWA Jun
[S/G] SERIALGAMES Inc.
TEL: 03-5812-0980
FAX: 03-5812-0970
twitter: http://twitter.com/serialgames
twitter: http://twitter.com/flaver_sg
mailto: j...@serialgames.co.jp
fla:ver http://flaver.jp/
WebCapS http://www.serialgames.co.jp/fun.html#WebCapSeria


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hHxTy
------------------------------------------------------[freeml byGMO]--

ありい

unread,
Dec 28, 2011, 5:28:55 AM12/28/11
to delphi...@freeml.com
 細川さん、こんばんは。
 解説ありがとうございます m(__)m

> nil.Free っていう訳では無いのです。
(略)


> Free のコードを見ると Self が nil かどうかを調べています。
> これはインスタンスごとのデータが nil なのかどうか、ということで、コード
> 自体は存在しているのです。

 はい。こちらも「感覚的に」そうかなぁ~と思っていました。

 nil.Freeはコードを見た時に、例えばJavaなら「ぬるぽ」だろ
的な気持ち悪さを表現しました。

 Freeの実装で、Selfがnilかを調べていてnilでない場合には
Destroyを呼んでいるのは判ったのですが、そもそもどうやって
obj(中身nil)がTObject.Freeに辿りつくことができるのかが、
謎だな~と思うのです。

#今までの気持ち悪さの主因です。

 obj(中身nil)がFree以外のメソッドを呼んだらアドレス違反に
なりますよね?

#Freeの場合はDelphi内部で宜しく取り計らってくれているんだ
#ろう、程度の認識です。これをシンタックスシュガーと表現し
#ました。

 そして細川さんの解説の、

> TMethod ヘルプとかをみると判りますがメソッドポインタというのは2つに別れ
> ています。
> コードへのポインタと、インスタンスごとのデータへのポインタです。
> TMethod でいうと
>
> TMethod.Code
> TMethod.Data
>
> です。
> それぞれポインタになっています。

...という部分を拝見して、インスタンスが見つからない(objが
nilの)場合に、Freeに限りTObject.Freeのコードに流れる細工が
あるのかなぁ、と推測している次第です。

 ...外していますでしょうか?(^^;

 また、今となっては難しいかもしれませんが、Delphiの内部的
な動きを解説している書籍などで良い本がありましたら紹介して
頂けないでしょうか(日本語で...) m(__)m

 解説して頂いた上にお願いで恐縮ですが...

 よろしくお願いします m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=hH05F
------------------------------------------------------[freeml byGMO]--

高木太郎

unread,
Dec 28, 2011, 8:54:57 AM12/28/11
to delphi...@freeml.com
 こんばんは、イマジオムの高木です。

ありいさん:


>  nil.Freeはコードを見た時に、例えばJavaなら「ぬるぽ」だろ的な
> 気持ち悪さを表現しました。
>
>  Freeの実装で、Selfがnilかを調べていてnilでない場合には
> Destroyを呼んでいるのは判ったのですが、そもそもどうやって
> obj(中身nil)がTObject.Freeに辿りつくことができるのかが、
> 謎だな~と思うのです。
>

>  obj(中身nil)がFree以外のメソッドを呼んだらアドレス違反に
> なりますよね?

 お気持ちは非常にわかりますが、実際にはちょっと違います。 試しに
下記のコードを試してみてください。

{$APPTYPE CONSOLE}

type TSomething=
class(TObject)
procedure WriteMessage;
end;

type TMainForm=
class(TForm)
Button:TButton;
procedure ButtonClick(Sender:TObject);
end;

procedure TSomething.WriteMessage;
begin
WriteLn('Hello!');
end;

procedure TMainForm.ButtonClick;
var Something:TSomething;
begin
Something:=nil;
Something.WriteMessage;
end;

 ボタンを押すと、Something.WriteLog でアクセス違反が生じるかと
思われるでしょうが、実際にはコンソール画面に Hello! と表示
されます。

 なぜかと言うと、コンパイラは Something が TSomething であると
いうことをしっかり理解していて、Something.WriteMessage 文を
TSomething.WriteMessage メソッドの呼び出しに置き換えてくれる
からです。 WriteMessage メソッドでは、Something インスタンスを
まったく使っていないので、(この例のように)たとえインスタンスが
存在していなくても問題なく動作します。 つまり──

  1.メソッドの呼び出し(エントリアドレスの取得)だけなら、
    インスタンスの存在は必須ではない。

  2.未作成オブジェクトや破棄済みオブジェクトのメソッドを
    呼び出してアクセス違反が生じるのは、そのメソッドが
    何らかの形でインスタンスを使っているから。

──ということになります。

 メソッドのエントリアドレスは、コンパイルされる時点ですべて
決めることができます。 したがって実行時に、エントリアドレスが
見つからないという理由でアクセス違反が起きることは、通常は
(DLLでも使っていなければ)ありません。

 アクセス違反が起きるのは「メソッドがインスタンスの存在を前提と
した作りになっているのに、実際にはインスタンスが存在していない」
場合です。 Free の場合には、インスタンスの存在を前提として
いない(インスタンスへのポインタが nil かどうか事前にチェック
している)ので、問題が起きないというわけです。 特にコンパイラが
Free を特別扱いしているわけではありません。


――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hH2Jn
------------------------------------------------------[freeml byGMO]--

ありい

unread,
Dec 29, 2011, 2:24:46 AM12/29/11
to delphi...@freeml.com
 高木さん、こんにちは。
 度々の解説ありがとうございます m(__)m

>  なぜかと言うと、コンパイラは Something が TSomething であると
> いうことをしっかり理解していて、Something.WriteMessage 文を
> TSomething.WriteMessage メソッドの呼び出しに置き換えてくれる
> からです。 WriteMessage メソッドでは、Something インスタンスを
> まったく使っていないので、(この例のように)たとえインスタンスが
> 存在していなくても問題なく動作します。 つまり──
>
>   1.メソッドの呼び出し(エントリアドレスの取得)だけなら、
>     インスタンスの存在は必須ではない。
>
>   2.未作成オブジェクトや破棄済みオブジェクトのメソッドを
>     呼び出してアクセス違反が生じるのは、そのメソッドが
>     何らかの形でインスタンスを使っているから。

 実際に試してみました!

 SomethingがTSomethingであるということは型指定から自明なので、
「全ての」メソッド呼び出しで、

1)自クラス(インスタンスでない)のメソッドに辿りついて実行

 →細川さん解説の「TMethod...コードへのポインタと、インスタンス
  ごとのデータへのポインタです。」の部分ですね!

2)実行して、作ってもいない自分のフィールドにアクセスすると
  アドレス違反が発生する

※多態とかは考えていません。ざっくりとしたイメージです。

 極端なことを言うと、VCL含めて「全ての」メソッド呼び出しの頭
にif Self = nil then exit; と入れておけば、生成漏れによるアド
レス違反のない世界が構築できるって事ですね!

#プロパティやフィールドも全てSetter/Getter作って同じつくりに
#すれば(^^;

##他にも穴があるかもしれませんが...(^^;;;

> Free の場合には、インスタンスの存在を前提として
> いない(インスタンスへのポインタが nil かどうか事前にチェック
> している)ので、問題が起きないというわけです。 特にコンパイラが
> Free を特別扱いしているわけではありません。

 上記を含めて、とても納得することができました。

#それはそれとして「インスタンスは作って操作、しっかり解放」が
#キホン、なのは肝に銘じるべきですね!

 高木さん、細川さん、ありがとうございました m(__)m おかげさま
で新年をすっきり気持ちよく迎えられそうです(^^)

 遊歩人さんも、きっかけを下さりありがとうございました m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
毎日豪華プレゼントキャンペーン開催中!くまポン
http://ad.freeml.com/cgi-bin/sa.cgi?id=hH9eK
------------------------------------------------------[freeml byGMO]--

まっちゃん

unread,
Dec 29, 2011, 2:36:08 AM12/29/11
to delphi...@freeml.com
松野と申します。

高木さんの解説はいつも本当に目から鱗です。

これらの処理を行っているのは Delphi のコンパイラです。
コンパイラの立場に立てば至極当たり前の事ですが、中々
理解するのが難しいです。

大変参考になりました。
これからもよろしくお願いいたします。


--
Atsushi Matsuno / HEXARD INC.
E-mail: mat...@hexard.co.jp


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hH9kL
------------------------------------------------------[freeml byGMO]--

Reply all
Reply to author
Forward
0 new messages