C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[13]: C# 大量データのファイル出力でのメモリ使用量について


(過去ログ 64 を表示中)

[トピック内 15 記事 (1 - 15 表示)]  << 0 >>

■37282 / inTopicNo.1)  C# 大量データのファイル出力でのメモリ使用量について
  
□投稿者/ poro (1回)-(2009/06/16(Tue) 16:18:59)

分類:[C#] 

みなさんはじめまして

今、新人研修で
DB上のデータをテーブル毎にテキストに出力するといった
簡単なプログラムを作成しているのですが、

大量データを出力する際に、以下のような問題が発生して困っています。
会社の先輩に聞いても、イマイチ解決策を出せないようです。

******************************************************
StreamWriterを利用し、
大量のデータ(100万件以上)をCSVなどのテキストに出力した場合、

出力するたびに使用メモリ(※タスクマネージャ上)が増加して、
OutOfMemoryが発生している状況です。

また、正常に処理が完了したとしても、
タスクマネージャ上のメモリがリリースされなくて困っています。

StreamWriterのインスタンスを一度だけ生成し、
While文でStreamWriter.WriteLine()を利用しています。

また、マルチ(2スレッド)で動かしています。

一方のスレッドでは、
他方のスレッドが持つテーブル毎の出力状況を監視し、
Windows画面上に出力し、

もう一方のスレッドでは、
DBからデータを抽出し、ファイル出力しています。⇒ ここで問題が起こっています。

マルチで動作させていることも問題になるのでしょうか?

Disposeなど、リソースの解放処理は確実に行っていますが、
GC.Collect()などの明示的な解放は行っていません。

できれば、CSV出力時に使用メモリを増加しないような作りにしたいのですが、
何か解決策があるのであれば、教えていただけないでしょうか?

【環境】
  C#(WindowsApplication作成)
  VisualStudio2005

******************************************************


引用返信 編集キー/
■37284 / inTopicNo.2)  Re[1]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ 囚人 (386回)-(2009/06/16(Tue) 16:32:05)
>もう一方のスレッドでは、
>DBからデータを抽出し、ファイル出力しています。⇒ ここで問題が起こっています。

この「DBからデータを抽出」を一気に100万件やってるからじゃないですか?
DB から少しずつ読み出しながら CSV に少しずつ書き出すってのがいいんじゃないでしょうか。

引用返信 編集キー/
■37285 / inTopicNo.3)  Re[2]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ なちゃ (299回)-(2009/06/16(Tue) 16:51:26)
ざっとプログラムの骨格を示してみてはどうでしょう?
多分囚人さんの書かれてるような事だと思いますが。

というか典型的とも言えますね。
引用返信 編集キー/
■37286 / inTopicNo.4)  Re[2]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (2回)-(2009/06/16(Tue) 16:51:54)
No37284 (囚人 さん) に返信
> >もう一方のスレッドでは、
> >DBからデータを抽出し、ファイル出力しています。⇒ ここで問題が起こっています。
>
> この「DBからデータを抽出」を一気に100万件やってるからじゃないですか?
> DB から少しずつ読み出しながら CSV に少しずつ書き出すってのがいいんじゃないでしょうか。
>

回答ありがとうございます。

100万件のレコードを抽出する際は、
Adapterを利用せずにDataReaderを利用して、
その中で出力処理を行っておりますので、
DBからのデータ抽出のところというより、
ファイル出力に問題があるのではないかと思っています。

もちろん囚人さんのご意見の通り、
少量のデータを出力させるという案も、
代替案として考慮すべきことかと思いますが、

大量データを出力するという仕様は、
今後、実際のプロジェクトに入れたときに役に立つかなと思うので、
メモリ消費量を抑える技術を学びたいです。


引用返信 編集キー/
■37287 / inTopicNo.5)  Re[3]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ すがり (44回)-(2009/06/16(Tue) 16:55:36)
DBから100万件データ取得の時点でメモリほとんど使い切っているという可能性は?
引用返信 編集キー/
■37288 / inTopicNo.6)  Re[4]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (3回)-(2009/06/16(Tue) 17:07:28)
No37287 (すがり さん) に返信
> DBから100万件データ取得の時点でメモリほとんど使い切っているという可能性は?

⇒ すがりさん、返信ありがとうございます。
  DataAdapterを利用しているわけではありません。


⇒ なちゃさん、返信ありがとうございます。
  以下にコードを転記します。



// ************
// 出力箇所(※その部分を抜き出しています)
// ************
DataTable cvrtTable = this.PrepareForConvert(); ※1

using (DataReader reader = this.GetConvertDataReader()) ※2
{

// 事前チェック
if (reader == null || string.IsNullOrEmpty(fileName))
return false;

// ファイル出力
string filePath = Path.Combine(ShareInfo.Instance.InFilePath, fileName); ※3
using (FileWriter fileWriter = new FileWriter(filePath)) ※4
{
while (reader.Read())
{
DataRow cvrtRow = this.ConvertValue(reader, cvrtTable.NewRow()); ※5

// NULLレコード出力対象外
if (cvrtRow != null)
fileWriter.AddCsvDataToEOF(cvrtRow); ※6 }
}
}

※1…出力用の列の枠を設定するだけです。
※2…this.GetConvertDataReader()でDB抽出時のDataReaderを拾ってきます。
  ちなみにDataReaderはDB読込みだけでなくファイル読込みも考慮しているため、
  IDataReaderをインプルした、私が作成した独自のクラスです。
※3…出力先を決めているだけです。
※4…FileWriterは独自に作成したクラスです、コンストラクタで一度だけStreamWriterをnewしています。
※5…出力用のデータを詰めているだけのメソッドです。
※6…実際のファイルの書き込み部分です。以下にメソッド内容を転記します。

// ************
// ファイルの書き込み部分
// ************
# region CSVファイルへデータ出力
/// <summary>
/// CSVファイルへデータ出力
/// (※ファイル末端へのデータ追加)
/// </summary>
/// <remarks>
/// CSVファイルの末端へデータを追記する。
/// </remarks>
/// <param name="row">出力データ</param>
public void AddCsvDataToEOF(DataRow row)
{

StringBuilder sb = new StringBuilder();
for (int i = 0; i < row.ItemArray.Length; i++)
{
sb.Append(row[i]).Append(",");
}

string str = sb.ToString();
sw.WriteLine(str.Substring(0, str.Length - 1));

}
# endregion

引用返信 編集キー/
■37289 / inTopicNo.7)  Re[5]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (4回)-(2009/06/16(Tue) 17:08:42)
転記したら
インデントがめちゃくちゃになってしまいました。
申し訳ありません。
引用返信 編集キー/
■37291 / inTopicNo.8)  Re[6]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ 囚人 (387回)-(2009/06/16(Tue) 17:58:39)
>もちろん囚人さんのご意見の通り、
>少量のデータを出力させるという案も、
>代替案として考慮すべきことかと思いますが、
>
>大量データを出力するという仕様は、
>今後、実際のプロジェクトに入れたときに役に立つかなと思うので、
>メモリ消費量を抑える技術を学びたいです。

あー、勘違いさせたかな。
メモリに保持しておかないで、少量ずつファイルに追記していくという意味ですが。


public void AddCsvDataToEOF(DataRow row)
{

StringBuilder sb = new StringBuilder();
for (int i = 0; i < row.ItemArray.Length; i++)
{
sb.Append(row[i]).Append(",");
}

string str = sb.ToString();
sw.WriteLine(str.Substring(0, str.Length - 1));

}

ここにある sw って何でしょ?

引用返信 編集キー/
■37292 / inTopicNo.9)  Re[7]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (5回)-(2009/06/16(Tue) 18:11:19)
囚人さん
コメントありがとうございます。

勘違いしちゃいました。
すいません。

swは、StreamWriterです。
(※4参照)

Disposeで、
Close()しています。

ちなみに作りとしては、
転記した出力箇所を抽象クラスのメソッドとして実装しています。

また、以下抽象メソッドを用意し、テーブル毎のサブクラスで実装させています。
PrepareForConvert()
GetConvertDataReader()
ConvertValue()

問題があれば、ご指摘よろしくお願いします!!

いわゆるテンプレートメソッドの作りにしています。
引用返信 編集キー/
■37293 / inTopicNo.10)  Re[8]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ なちゃ (300回)-(2009/06/16(Tue) 18:23:25)
ぱっと見た感じだけではそうまずそうな所は無さそうですねー
携帯なんで見落としはあるかも知れませんが。

FileWriterを使わずにただ読み込んで捨てるだけならどうなりますか?
その結果で抽出か書き込みどっちが悪いか分かるかも知れません。

引用返信 編集キー/
■37296 / inTopicNo.11)  Re[9]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (6回)-(2009/06/16(Tue) 18:35:45)
なちゃさん

返信ありがとうございます。

なちゃさんのご指摘通り、
Whileの中をコメントして処理を実行してみました。

メモリ使用量は、増加せずに大丈夫でした。
やはり、ファイル出力に問題があるようです。

引用返信 編集キー/
■37297 / inTopicNo.12)  Re[10]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (7回)-(2009/06/16(Tue) 18:54:17)
試しに、
以下のようにしてもメモリは増加していってしまします。


       public void AddCsvDataToEOF(DataRow row)
        {

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < row.ItemArray.Length; i++)
            {
                sb.Append(row[i]).Append(",");
            }

            string str = sb.ToString();
            sw.WriteLine(str.Substring(0, str.Length - 1));
            sw.Flush();

            count++;
            if (count % 10000 == 0)
            {

                sw.Flush();
                sw.Dispose();
                GC.Collect();
                Encoding enc = Encoding.GetEncoding("Shift_JIS");
                sw = new StreamWriter(outputFilePath, true, enc);
                sw.AutoFlush = true;

            }

        }

引用返信 編集キー/
■37298 / inTopicNo.13)  Re[11]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ なちゃ (301回)-(2009/06/16(Tue) 19:22:51)
少しずつ確認していきましょう。

DataRow cvrtRow = this.ConvertValue(reader, cvrtTable.NewRow()); ※5

この行を復活させるとどうなりますか?
FileWriterへの書き込みはなしのままで。

引用返信 編集キー/
■37300 / inTopicNo.14)  Re[12]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (9回)-(2009/06/16(Tue) 19:42:18)
なちゃさん
コメントありがとうございます。

なちゃさんのおかげで
解決することができました。

問題は、 cvrtTable.NewRow()の部分でした。

とりあえず、参照渡しにし、
以下のように修正することで解決しました。

*****************************************************************
using (FileWriter fileWriter = new FileWriter(filePath))
{
DataRow cvrtRow = cvrtTable.NewRow();
while (reader.Read())
{
this.ConvertValue(reader, cvrtRow);

// NULLレコード出力対象外
if (cvrtRow != null)
fileWriter.AddCsvDataToEOF(cvrtRow);
}
}
*****************************************************************

本当にありがとうございました!!!
完全に見落としていました。

やはり、原因は必ずありますね。

なちゃさん、囚人さんが協力していただいて、
こうして解決していただくことができて、
本当に励みになりました!!
早く一人前になれるように頑張ります。
引用返信 編集キー/
■37301 / inTopicNo.15)  Re[13]: C# 大量データのファイル出力でのメモリ使用量について
□投稿者/ poro (10回)-(2009/06/16(Tue) 19:44:13)
解決済みにします。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -