動的配列を乗り切ったのですが、今度はメモリが足りなくなる現象で足止めを
喰ってます。
2006/10/13 000000 5201 @_ 0.78050
2006/10/13 000000 5202 @_ 0.81794
2006/10/13 000000 5203 @_ 0.79767
2006/10/13 000000 5204 @_ 0.49183
2006/10/13 000000 5205 @_ 0.49823
2006/10/13 000000 5206 @_ 0.47400
2006/10/13 000010 5201 @_ 0.78043
2006/10/13 000010 5202 @_ 0.81790
2006/10/13 000010 5203 @_ 0.79762
2006/10/13 000010 5204 @_ 0.49173
2006/10/13 000010 5205 @_ 0.49808
2006/10/13 000010 5206 @_ 0.47390
このような形式のデータが約55000行ほどあり、TStringListのDelimiterに半角
スペースを指定して、切り分けながら配列に取り込んでいます。が、途中で「メ
モリが足りません」とOSに言われて、止まってしまいます。
配列は文字列型なのですが、上の文字列は最高でも10文字しかありません。半角
で1文字1バイトですので、
55000行 x 5列 x 10バイト = 2750000バイト = 2.75MB
私のパソコンは512MB積んでますので、ゴミみたいなデータ量でしかないと思う
のですが、止まってしまいます。ちなみに、上のように12行にすると、処理は一
瞬で終わります。
何が起こってるのでしょうか。
開発環境は Delphi2005 + WinXPSP1です。
--
ron
mailto:r...@ebetsu.arrow.jp
先ほどのソースの感じから、
ループ内で TStringList を何回も Create してませんでしょうか?
ループ外で Create して、finally で
確実に Free するようになっていますでしょうか?
こちらで単純に 55000 件の動的配列を処理させましたが、
特別エラーにはなりませんでした・・・めちゃくちゃ待たされましたが(^^;
あと、都度 SetLength をしてしまうと、
恐ろしいほど処理が重くなりますので、
必要な要素数が計算で算出できるならば、
最初の初期化の段階で、一気に確保してしまう方が良いです。
実際、テストしてみたら、55000件の要素を
先に確保した場合、一瞬で帰ってきました。
どうでしょうか?
ron さんのメールは 2006年12月21日 17時15分 頃、
『[Delphi:89068] メモリが足りなくなる』という題名で受信されました。
---
---
(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)
MUROHASHI Yuzo
ymuro...@hochiki.co.jp
(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)
MUROHASHI Yuzo wrote (2006/12/21 17:30):
> 先ほどのソースの感じから、
> ループ内で TStringList を何回も Create してませんでしょうか?
です。while文のなかでCreateとFreeをくりかえしています。・・・昔からそう
いうコードを書いてるのですが、あんまり良くないっぽいですね。(^^;;;
> ループ外で Create して、finally で
> 確実に Free するようになっていますでしょうか?
こちらに直した方が良いってことですね。φ(.. )
> こちらで単純に 55000 件の動的配列を処理させましたが、
> 特別エラーにはなりませんでした・・・めちゃくちゃ待たされましたが(^^;
うわ、実験してくれましたか。ありがとうございます。m(_ _;)m
> あと、都度 SetLength をしてしまうと、
> 恐ろしいほど処理が重くなりますので、
> 必要な要素数が計算で算出できるならば、
> 最初の初期化の段階で、一気に確保してしまう方が良いです。
了解です。なんというか、動的配列使った方が格好いいかなっていう、しょうも
ない動機で使っただけですので、宣言の段階で配列を確保してしまうようにします。
大体60000~70000も確保すれば十分なのはわかってますので。
> 実際、テストしてみたら、55000件の要素を
> 先に確保した場合、一瞬で帰ってきました。
これからパソコンショップに行かねばなりません。こちらでの実験には、すこし
間が開きます。
申し訳ないですが、少々お待ちください。m(_ _;)m
--
ron
mailto:r...@ebetsu.arrow.jp
追記です。
> こちらで単純に 55000 件の動的配列を処理させましたが、
> 特別エラーにはなりませんでした・・・めちゃくちゃ待たされましたが(^^;
2回目実行してみたら、メモリが足りなくなりました(^^;
タスクマネージャを見ると、ぐんぐん物理メモリが減っていきますね。
連続して SetLength することで、
メモリマネージャのメモリ開放が追いつかないのかな・・・?
先述の通り、先に必要要素数を計算して
一気に確保する方がいいかもしれないですよ。
メモリの断片化のせいみたいですね。
SetLength とオブジェクトの生成/解放を繰り返し行っているためでしょう。
以下が参考になると思います。
「D5のバグ? TStringListでEOutOfMemory。」
http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=080490
Quoting MUROHASHI Yuzo <ymuro...@hochiki.co.jp>:
----------
中村拓男
ってことは Delphi 2006 だと起きないんですかね.
大きいメモリと小さいメモリの管理が別立てになっていると聞いたような...
どちらにしろメモリの管理のコストは高いので、まとめてやるのが無難なんで
しょうねえ...
こんなのは如何でしょうか。
ImportValue は TStringList で外でデータが入っているとします。
Const
AddSpan=500;
Var
ValueString:Array of String;
ValueCount:Integer;
Begin
ValueCount := 0;
SetLength(ValueString,AddSpan);
For i := 0 to ImportValue.Count-1 Do Begin
//配列に入れた数を数えておく
Inc(ValueCount);
//AddSpan で指定された数単位で配列を「余分に確保」していく。
If (ValueCount mod AddSpan) = 0 then
SetLength(ValueString,ValueCount+AddSpan);
//ここで配列に入れる、加工したり色々
ValueString[ValueCount-1] := ImportValue[i];
End;
//最後に余分に確保された配列を削る意味で再設定
SetLength(ValueString,ValueCount);
End;
メーラ上でそのまま書いていますので、間違っていたらすんません。
最初に、500 確保しておいて、500 入れたら追加で 500 確保して・・・
を繰り返しています。1個づつ SetLength() するより早くてメモリ不足に
なる状況は避けられます(当然、これでも SetLength() を行う
回数が増えれば同じようなエラーにはなります)
500 を増やせばそれだけ SetLength() する回数は減りますが
余分なメモリを使います。減らせばメモリ消費量は抑えられますが
SetLength() する回数は増えます。
根本的な解決にはなっていませんが、数万行程度ならこれで
充分対応できるんじゃないかと思います。
Software Labo A.K.I Software
3-28-89, Sanaru-dai, Hamamatsu-citu, Shizuoka, Japan
Homepage : http://akisoftware.jp/
Quoting nekomaho <neko...@gmail.com>:
> ねこまほです.
>
> ってことは Delphi 2006 だと起きないんですかね.
> 大きいメモリと小さいメモリの管理が別立てになっていると聞いたような...
>
.NET なら起きないでしょうね。Win32版は改善されているらしいですが
詳細は調べてません。
> どちらにしろメモリの管理のコストは高いので、まとめてやるのが無難なんで
> しょうねえ...
>
動的配列は連続したメモリに置かれるので、SetLengthすると
1) 新しい領域の確保
2) 内容のコピー
3) 古い領域の破棄
となるので、頻繁にSetLengthするとメモリ管理のコストと共に
コピーのコストもものすごいことになります。
また、要素数が 55000 X 5 = 275000 とすると文字列では要素の
大きさは4バイトですから(文字列本体は別のメモリに置かれる)、
動的配列の大きさは1MB超程度となります。
動的配列が少しずつ大きくなっていった場合、上記の 3) の領域が
メモリ管理で再利用されずらいので、メモリの使用量がものすごい
勢いで増大すると思います(おそらく終盤では1回1MBくらい)。
ですので、動的配列を少しずつ伸張するために SetLength を行うのは
避けたほうがよいです。
TList や TStringList も連続メモリ(配列)に要素のポインタを置いてますが、
こちらは領域が足りなくなると それまでの大きさに 1/4 を継ぎ足した大きさを
確保するようにしてます。ですから 要素数が増えると急激に配列の伸張回数が
減るようになっています。
----------
中村拓男
先ほど過去ログの内容を読みました。
なるほど、伸張時に新しい領域を確保するには、
元の領域より連続した大きい領域を確保しなくてはならないため、
どんどんフラグメンテーションが発生することが原因なんですね。
小生のプログラムの場合、動的配列はまず使わず、
TStringList などのリスト系クラスを使っていますが、
今回のような事象には未だ遭遇したことがありませんでした。
勉強になります、ありがとうございました(^^)
tknakamura さんのメールは 2006年12月22日 09時11分 頃、
『[Delphi:89073] Re: メモリが足りなくなる』という題名で受信されました。
---
---
すぐに実験するようなことを言っておきながら、日を越してしまいました。昨日
はDebian本読んでて力尽きてしまいました。(学園アリスも見ました。(^^;;;)
失礼しました。m(_ _;)m
MUROHASHI Yuzo wrote (2006/12/22 9:27):
> 小生のプログラムの場合、動的配列はまず使わず、
> TStringList などのリスト系クラスを使っていますが、
> 今回のような事象には未だ遭遇したことがありませんでした。
貴殿の仰る通り普通の配列にしたら、すんなりと動きました。
ここのスレッドがらみで中村さんの示されたページも見てみましたが・・・まぁ
難しいです。(^^;;;
動的配列を使うと、いかにもメモリを節約してるみたいで格好いいと思ったので
すが、自爆のようですね。
いろいろとありがとうございました。m(_ _;)m
--
ron
mailto:r...@ebetsu.arrow.jp