とあるOracleDBを読み込んでStringGridに列を保存するアプリあります。
15万件読み込んで完了し、Windows全体のメモリ消費は全体で2.5GBほど(アプリ自体で2GB程度)でした。
XMLを読み込んで自力で解読しStringGridに列を保存するアプリがあります。
3万件読み込んで、メモリ不足になります。その時点のWindows全体のメモリ消費は1.3GB程です。アプリのみでは1GBも消費していないです。(こちらはDB関係は使っていないです)
XML読み込みで今回10万件処理する必要があります。
XML(200行程度)をMEMOにLoadFromFileし、UTF8toAnsiしAnsiStringにコピーし、必要なタグを取得(AnsiString.Pos("<tag>")などとやっています)、StringGridに列表示する、くらいで
どこかに見落としもあるでしょうが、あまり大したことはやっていないのです。
どこかの解放不足なのかなとか見ているのですが、だとすると消費メモリはもっと多くなるはず?
どんな事が考えられますでしょうか?この命令は使わない方が良いとか?
メモリ不足とは単に、タスクマネージャのプロセズタブのPF使用量でもなく、他の不足でもあるのでしょうか?
使った事が無いのですが統合デバッガとかで分かりますかね?
もう一度ソースを見直して色々そぎ落とそうと思いますが、メモリ不足ってなんだろうなぁ?と。
宜しくお願い致します。
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZan6
------------------------------------------------------[freeml byGMO]--
しばじゅんたさん:
> とあるOracleDBを読み込んでStringGridに列を保存するアプリあります。
> 15万件読み込んで完了し、Windows全体のメモリ消費は全体で2.5GBほど
> (アプリ自体で2GB程度)でした。
>
> XMLを読み込んで自力で解読しStringGridに列を保存するアプリがあります。
> 3万件読み込んで、メモリ不足になります。その時点のWindows全体の
> メモリ消費は1.3GB程です。アプリのみでは1GBも消費していないです。
> (こちらはDB関係は使っていないです)
>
> XML読み込みで今回10万件処理する必要があります。
> XML(200行程度)をMEMOにLoadFromFileし、UTF8toAnsiしAnsiStringに
> コピーし、必要なタグを取得(AnsiString.Pos("<tag>")などとやって
> います)、StringGridに列表示する、くらいでどこかに見落としもある
> でしょうが、あまり大したことはやっていないのです。どこかの解放不足
> なのかなとか見ているのですが、だとすると消費メモリはもっと多くなるはず?
>
> どんな事が考えられますでしょうか?この命令は使わない方が良いとか?
> メモリ不足とは単に、タスクマネージャのプロセズタブのPF使用量でも
> なく、他の不足でもあるのでしょうか?
>
> 使った事が無いのですが統合デバッガとかで分かりますかね?
> もう一度ソースを見直して色々そぎ落とそうと思いますが、メモリ
> 不足ってなんだろうなぁ?と。
メモリ不足、当方でもよく悩まされます。 その対策にあたり、私は
コレクションクラスのメモリ管理方法を意識する(つまりVCLの
ソースコードに目を通しておく)ようにしています。 これによって
メモリ使用の無駄を把握することができます。
一つ例を挙げると、「TList オブジェクトに Add メソッドで要素を追加
していく場合、本来必要な量の約2倍のメモリが必要になる」ということが
あります。 これは TList が、実際には要素をリンクリストではなく、
配列として保持しており、現在の配列サイズを越えて要素を追加しようと
すると、より大きい配列の確保と配列全体の複製が行われるためです。
#TList の SetCapacity と Grow を見てみてください。
TList の現在の実装では、新しい配列は古い配列の1.25倍の大きさに
なります。 ですから現在の配列のサイズが100要素で、101番目の
要素を追加しようとした場合、瞬間的に225要素分のメモリが必要に
なるわけです。 また配列の移し替えを何度も行うと、メモリ断片化の
問題も生じるので、実際にはもっと早くメモリ不足になります。 配列
全体のコピーによるパフォーマンスの低下も無視できません。 同様の
ことは、たぶん TStringList や TGrid でも起きると思います。
これらの問題を避ける一つの方法は、TList.Grow メソッドをオーバ
ライドし、新しい配列を古い配列の4倍程度にすることです。 これに
よって配列の移し替えの頻度が減り、メモリ断片化とパフォーマンスの
問題はかなり改善します。
より良い方法として、あらかじめ配列の最大要素層を把握しておき、
SetCapacity メソッドで、ピッタリのサイズのメモリしか確保しない
ようにすることもできます。 これにより、配列の複製を大きく抑制
することができます。
また思い切って、配列の内容をディスクに書き出す手もあります。
こうすれば配列を拡大する際に二つの配列をメモリに置かずにすみます。
この場合のポイントは、メモリでの要素保持イメージを、ディスクに
書き出すフォーマットと一致させておくことです。 これによって
ディスクI/Oを高速化することができます。 高速化には、TFile-
Stream にキャッシュ機能を付加する方法も有効です。
自作のアプリケーションでは、複雑なデータを保持するのに複数の
TList を使っているかもしれません。 しかしできればこれはやめ、
一つの TList で複数のレコード型の要素を管理する方が、メモリの
利用の点では効率的です。
……なんだかまとまりがなくなってきましたが、要するに「VCLの
メモリ管理方法を知ることが第一。 解決策はいろいろある」という
ことです。 お書きの現象だけからは、メモリリークなどの可能性も
否定できませんが、上記のような「TList の実装から来る壁」も
ありますので、ご参考にしていただければと思います。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒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=hZb72
------------------------------------------------------[freeml byGMO]--
高木さんの回答と被ってしまいますが。
> どんな事が考えられますでしょうか?この命令は使わない方が良いとか?
メモリ領域の再確保が頻繁に起きないように、Capacityプロパティ
などであらかじめ領域サイズを必要量確保するようにすることで
ある程度防ぐことができます。また比較的古いバージョンのDelphiを
お使いであればメモリマネージャをFastMMなどの新しい(比較的メモリが
豊富な環境に合わせた)ものに差し替えることでも問題が緩和されるかも
しれません。
FastMM | Free Development software downloads at SourceForge.net
http://sourceforge.net/projects/fastmm/
---
東洋テクニカルシステム株式会社 システム開発部 福士 光
Hikaru Fukushi (Toyo Technical System Inc.)
mailto:fuk...@tts-inc.co.jp
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZbGg
------------------------------------------------------[freeml byGMO]--
大変勉強になります。
TStringListなどは非常に好きで多用しているのですが
このAPPに関してはとにかく大量処理なので new delete add
等を極力使わないようにはしており、1件1件の処理にはありません。
StringGridがColCount=300なのでやはりこれなのかなぁと思います。
画面上で集計を行う為にDiskに吐き出す事も出来ず、
痛いのですが何とかやりくりして別な方法を取らなければいけないのでしょうね。
精緻なご返事に感動しています!!!ありがとうございます。
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZcgb
------------------------------------------------------[freeml byGMO]--
しばじゅんたさん:
> StringGridがColCount=300なのでやはりこれなのかなぁと思います。
StringGrid が300列×10万行(3,000万セル)になって
しまうということでしょうか。 そうだとすると、これは大きい
ですね……
それでしたらデータを別に保持し、StringGrid には表示される
部分のみを持たせるのが(メモリ節約の観点では)いいと思います。
要するに StringGrid を(データ保持ツールではなく)データ表示
ツールと割り切って使うということです。
スクロール機能などを自前で作ったり、データを保持するための
クラスを新たに作ったりしなければなりませんが、逆に String-
Grid に直接関連する処理がかなり減るので、やってみると意外と
簡単だと感じられるのではないでしょうか。
先ほど申したディスクの援用もできるようになります。 データの
保持と表示を分けるメリットは多いので、ぜひご一考ください。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒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=hZdha
------------------------------------------------------[freeml byGMO]--
起動時に
StringGrid1->RowCount=60000;
StringGrid1->ColCount=300;
for( int r=0; r<StringGrid1->RowCount; r++ )
for( int c=0; c<StringGrid1->ColCount; c++ ) StringGrid1->Cells[c][r]="
";
とかして、一気に巨大化させておいて(Windowsメモリは2GBくらいになりましたが問題なし)
実行して見ると、メモリの変化は殆どなく、処理はさすがに速くなりいい感じでしたが、
2万件程でメモリ不足になってしまいました・・・。
StringGridの巨大化の問題ではなく別なところなのか?セルの内容が変わるので単に否定も出来ず。
こんなに使うのは1ユーザーだけで、年1回しか行わない、国の制度に変更が無ければ今年で終わる予定(トホホ)。
昨年までは3万件程で、今年は10万件を予定だと言われましたが本当は5万件ほどでは?などと。
専用にデータベース化するにも書き換えるにもこんな状態ですので何とも困ったなぁ状態です・・・
各所の処理を外してみて、どこが問題なのかを見つけてみようかと思います。
サブルーチンの中の変数をグローバル1つだけ持って行くとか。関係ないかな。
ありがとうございます。
(BCBなのがバレバレで済みません)
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
映画「僕達急行 A列車で行こう」ポーチプレゼント!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZdZq
------------------------------------------------------[freeml byGMO]--
しばじゅんたさんのところで起こっているメモリ不足とは、おそらく下記のような事だと思います。
簡単に説明する為、全メモリを4バイトとし全領域が未使用であるとします。
1234
**** -> 未使用領域
この状態では空きメモリは4バイトです。
この状態から1バイトメモリを使用したとします。
1234
^***
この状態では空きメモリは3バイトです。
さらにこの状態から2バイトメモリを使用したとします。
1234
^^^*
この状態では空きメモリは1バイトです。
ここで最初に使用した1バイトを解放します。
するとメモリ状態は下記のようになります。
1234
*^^*
この状態では空きメモリは2バイトですが、さらに2バイトのメモリを確保する事はできません。
メモリというのは連続した領域で無ければならないからです。
このような状態で2バイトのメモリを確保しようとすると、Windowsはメモリ不足というエラーを
出すことになります。
このような説明で如何でしょうか?
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZeu9
------------------------------------------------------[freeml byGMO]--
しばじゅんたさん:
> 起動時に
> StringGrid1->RowCount=60000;
> StringGrid1->ColCount=300;
> for( int r=0; r<StringGrid1->RowCount; r++ )
> for( int c=0; c<StringGrid1->ColCount; c++ )
> StringGrid1->Cells[c][r]=" ";
> とかして、一気に巨大化させておいて(Windowsメモリは2GBくらいに
> なりましたが問題なし)
>
> 実行して見ると、メモリの変化は殆どなく、処理はさすがに速くなり
> いい感じでしたが、 2万件程でメモリ不足になってしまいました・・・。
やっぱりセル数が(StringGrid の想定より)多すぎるのでは
ないでしょうか? 1,800万セル(18Mセル)もあると、
ちょっと長い文字列を入れれば、簡単に2Gバイトくらいになって
しまうように思います。
また結構 string 型(UnicodeString・AnsiString とも)は結構、
メモリを食います。 Delphi のヘルプ「メモリ管理」をご覧になると
わかりますが、文字配列の前に8~12バイトの管理領域、後に
ヌル文字が付くので、たとえ1文字を記憶するだけでも、10バイト
以上を使ってしまいます。 もし大容量のデータを扱うなら、
レコードからなるべく長い文字列をなくす(長さに上限があるなら短い
文字列型に変える。 入力内容が限られているなら列挙型や順序型に
変える)取り組みをしてはいかがでしょうか。
> こんなに使うのは1ユーザーだけで、年1回しか行わない、国の制度に
> 変更が無ければ今年で終わる予定(トホホ)。昨年までは3万件程で、
> 今年は10万件を予定だと言われましたが本当は5万件ほどでは?
> などと。専用にデータベース化するにも書き換えるにもこんな状態
> ですので何とも困ったなぁ状態です・・・
うーん、往々にしてありがちですね。 取りあえずそのユーザさんに、
メモリの増設を依頼するというのはいかがでしょう。 3月締め切りだと
すると、あまり時間がないでしょうし。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒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=hZfeY
------------------------------------------------------[freeml byGMO]--
不要な列設定を止めるなど色々とやってみました。
メモリ不足になるまでの読み込み件数に多少違いはあれど30000件程以上には
出来ず。
メモリ不足で読み込み処理停止後、次の集計処理(この30000件のGridを編集し
20*30000程の別のStringGridを成型する)が出来てしまうので、あれ?という発見をし、
それで、StringGrid1が30000件に達したらStringGrid2に保存するようにしたら
50000件読めてしまいました。Windows全体メモリは1.5GB程、アプリだけで1GB程か。
結局これは確かにメモリエラーかも知れませんが、
高木氏のおっしゃる通り、「StringGridにそんなにたくさん溜めるな」エラーかと。
なんだそうか、はいはい分かりましたという感じです。
取りあえず大改造になるのを避け、10万件テストデータを作り、
続StringGrid、続続StringGridに保存する方向にして(ちとださいですが)、
これでどこまでいけるか試してみます。
今年の集計(7月)の5万件は何とかなりそう、来年の10万件は、これでも駄目なら
メモリ追加してもらおうかと(「で、どれだけ追加すれば良いの?」「分かりません・・・」(笑))。
皆々様のサジェスチョン 、大変ありがたく思います。
また成果ありましたらご報告させて頂こうかと思います。
----- Original Message -----
From: "高木太郎" <delphi...@freeml.com>
To: <delphi...@freeml.com>
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZtIn
------------------------------------------------------[freeml byGMO]--
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.
Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls;
type
TForm3 = class(TForm)
StringGrid1: TStringGrid;
Memo1: TMemo;
procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure FormCreate(Sender: TObject);
procedure StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure StringGrid1DblClick(Sender: TObject);
procedure StringGrid1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form3: TForm3;
Data: array[0..300,0..60000] of String;
SaveCol,SaveRow: Integer;
implementation
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
begin
Data[1,1] := '1,1';
Data[1,2] := '1,2';
Data[1,3] := '1,3';
Data[2,1] := '2,1';
Data[2,2] := '2,2';
Data[2,3] := '2,3';
end;
procedure TForm3.StringGrid1Click(Sender: TObject);
begin
with StringGrid1 do
begin
if goEditing in Options then
begin
if (SaveCol <> -1) and (SaveRow <> -1) then
begin
Data[SaveCol,SaveRow] := Cells[SaveCol,SaveRow];
Cells[SaveCol,SaveRow] := '';
Options := Options - [goEditing];
SaveCol := -1;
SaveRow := -1;
end;
end;
end;
end;
procedure TForm3.StringGrid1DblClick(Sender: TObject);
begin
with StringGrid1 do
begin
if not (goEditing in Options) then
begin
SaveCol := Col;
SaveRow := Row;
Cells[Col,Row] := Data[Col,Row];
Options := Options + [goEditing];
EditorMode := True;
end;
end;
end;
procedure TForm3.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
StringGrid1.Canvas.TextOut(Rect.Left-2,Rect.Top+2,Data[ACol,ARow]);
end;
procedure TForm3.StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 13 then
begin
with StringGrid1 do
begin
if goEditing in Options then
begin
Data[Col,Row] := Cells[Col,Row];
Cells[Col,Row] := '';
Options := Options - [goEditing];
SaveCol := -1;
SaveRow := -1;
end
else
begin
SaveCol := Col;
SaveRow := Row;
Cells[Col,Row] := Data[Col,Row];
Options := Options + [goEditing];
end;
end;
end;
end;
end.
お世話様です。
皆々様のサジェスチョン 、大変ありがたく思います。
また成果ありましたらご報告させて頂こうかと思います。
MLホームページ: http://www.freeml.com/delphi-users
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZvyt
------------------------------------------------------[freeml byGMO]--
余計な茶々を、すみません m(_ _)m
MLホームページ: http://www.freeml.com/delphi-users
----------------------------------------------------------------------
毎日豪華プレゼントキャンペーン開催中!くまポン
http://ad.freeml.com/cgi-bin/sa.cgi?id=hZyfz
------------------------------------------------------[freeml byGMO]--