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

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

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

Re[8]: OutOfMemoryExceptionについて


(過去ログ 85 を表示中)

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

■50537 / inTopicNo.1)  OutOfMemoryExceptionについて
  
□投稿者/ おそら (1回)-(2010/06/10(Thu) 14:41:15)

分類:[.NET 全般] 

OutOfMemoryExceptionについて調べていて気になったので質問します。

http://msdn.microsoft.com/ja-jp/library/system.outofmemoryexception(VS.80).aspx
を見ると「プログラムの実行を継続するためのメモリが不足している場合にスローされる例外。」
との記述がありますが、これってどのような状況をさしているのでしょうか。

例えば以下のようなコードを適当に実行するとExceptionが発生します。

    StringBuilder test = new StringBuilder();
    test.Append("");←適当に300文字くらい

    for (long i = 0; i < 100000000000; i++)
    {
        test.Append(test.ToString());
    }

実行環境
 XP/SP2 メモリ2G

実行前は使用メモリ500M程度、iが18になった時にExceptionが発生します。
使用量は900M程度(タスクマネージャーにて確認)
この時なぜ発生するのか、何の単位でこのメモリーが不足しているのかって分かるものでしょうか。

因みにwindowsフォーム及びWebでも同様にエラーになります。

今後プロジェクトでメモリを大量に使用するソースを書く為、
どのような原因で発生するのか把握しておきたいのですが、ご教示よろしくお願いいたします。

引用返信 編集キー/
■50538 / inTopicNo.2)  Re[1]: OutOfMemoryExceptionについて
□投稿者/ れい (929回)-(2010/06/10(Thu) 15:40:19)
No50537 (おそら さん) に返信
> 今後プロジェクトでメモリを大量に使用するソースを書く為、
> どのような原因で発生するのか把握しておきたいのですが、ご教示よろしくお願いいたします。

どういうレベルで把握したいのかわからないので
まとはずれな回答かもしれませんが。

> http://msdn.microsoft.com/ja-jp/library/system.outofmemoryexception(VS.80).aspx
> を見ると「プログラムの実行を継続するためのメモリが不足している場合にスローされる例外。」
> との記述がありますが、これってどのような状況をさしているのでしょうか。

まさに、メモリが足りない状況をさしています。
必要な量のメモリを確保しようとして、確保できない時に投げられるのが普通です。

x86ならプロセスあたり2Gの制限があり、.NetやOSのDLLなどの分があるので
1GByteちょっとくらいしか使えません。

> test.Append("");←適当に300文字くらい

適当じゃなくて一文字とか二文字とか、特定したほうが再現性がありますよ。

> 実行前は使用メモリ500M程度、iが18になった時にExceptionが発生します。
> 使用量は900M程度(タスクマネージャーにて確認)
> この時なぜ発生するのか、何の単位でこのメモリーが不足しているのかって分かるものでしょうか。

300が倍々に増えていくので、i=18になった時点で
testは78643200文字=157286400Byte=157MByte入っています。

さらに同じだけ追加しようとしているので
testは少なくとも314MByte必要になります。

また、いちいちToStringしてStringインスタンスを生成しているので、
そのゴミが回収されずに残っています。
これが総計で同じ程度、100〜200MByte程度はあるでしょう。

また、StringBuilder内部でもバッファを拡張するたびにゴミが発生しており、
これも同程度あります。
さらに、追加時には内部でString変換がかかる場合があり、さらに2倍程度消費します。

200+200+400=800MByte以上消費した上でさらに314MByte以上を確保しようとしているので
失敗しているのでしょう。

GC.Collectしたり少し待ったりすればこれらのゴミは回収されますので、少し改善されます。
x64にすればプロセス辺りのメモリ制限は緩和されます。

引用返信 編集キー/
■50540 / inTopicNo.3)  Re[1]: OutOfMemoryExceptionについて
□投稿者/ なちゃ (448回)-(2010/06/10(Thu) 15:41:25)
要求されたメモリが確保できなかった場合です。
というのではなくてもう少し細かい話ですか?

まず根本的に、アプリケーションが確保できるメモリ量というのは、
マシンの物流メモリ搭載量とはあまり関係ありません。
32ビットプロセスではユーザ領域と言われるメモリ空間が基本的に限界2GBで、
もちろんアプリケーションそのものが全部使える訳ではなく、
.NETアプリケーションの場合は目安として1GBくらいの感じです。

また、巨大な配列などの連続メモリ領域は、ヒープの断片化などによって確保に失敗しやすいものです。
メモリ空間の空き容量がトータルでは十分あっても、
それが連続していないために確保できないといったことが起きるためです。

今回の例だと、ループ毎に約2倍のメモリを確保しようとしますから、
18ループ目なら25万倍以上のサイズのメモリを確保しようとします。
最初が300文字なら、300×2×25万で150MB以上です。
すでに使用中で外せないメモリが少なくともその半分のサイズありますが、
実際には確保したいメモリ量が倍々になっていくため、
推測ですが前に使った領域を再利用できない可能性があります。
この辺は、StringBuilderのメモリ領域の確保の仕方と、
CLRのメモリ管理の仕方の両方に依存するため、
実際に何が起こるかを予測するのは難しいですが。

例えばStringBuilderは、メモリを必要量の倍確保するような動きをしたと思います。

すると、本来必要なのは75MBプラス150MBのはずが、実際には
300MBプラス300MBくらいの確保をしようとしているかも知れません。
正確にはStringBuilderの内部の動きを探らないと分かりませんが、
もっとひどいことになってるかもしれません。

これくらいになると、連続メモリ領域の確保は失敗する可能性がかなり高くなります。


引用返信 編集キー/
■50542 / inTopicNo.4)  Re[2]: OutOfMemoryExceptionについて
□投稿者/ なちゃ (449回)-(2010/06/10(Thu) 15:47:19)
おっと、iは0スタートなので、私が書いた倍必要ですね。
最小でも150MBプラス300MBです。
再利用できてなくて確保量が倍なら300MBプラス600MBですね。

まあいずれにしてもStringBuilderの中身を確認してないので想像に過ぎませんが。

引用返信 編集キー/
■50543 / inTopicNo.5)  Re[2]: OutOfMemoryExceptionについて
□投稿者/ れい (930回)-(2010/06/10(Thu) 15:48:06)
No50540 (なちゃ さん) に返信
> 例えばStringBuilderは、メモリを必要量の倍確保するような動きをしたと思います。

追加する際には「現在の文字列長の倍」と「必要な長さ」の大きい方の分だけ確保します。
必要量の倍ではありません。

今回の
> test.Append(test.ToString());
は丁度倍ですので、
確保の効率としては最適となっています。

引用返信 編集キー/
■50545 / inTopicNo.6)  Re[3]: OutOfMemoryExceptionについて
□投稿者/ なちゃ (450回)-(2010/06/10(Thu) 15:50:54)
No50542 (なちゃ さん) に返信
> 再利用できてなくて確保量が倍なら300MBプラス600MBですね。

何度もすみません、この想定なら600MBプラス600MBになるかな?
まあどっちにしてもただの想像です。

引用返信 編集キー/
■50546 / inTopicNo.7)  Re[3]: OutOfMemoryExceptionについて
□投稿者/ なちゃ (451回)-(2010/06/10(Thu) 15:58:59)
No50543 (れい さん) に返信
> ■No50540 (なちゃ さん) に返信
>>例えばStringBuilderは、メモリを必要量の倍確保するような動きをしたと思います。
>
> 追加する際には「現在の文字列長の倍」と「必要な長さ」の大きい方の分だけ確保します。
> 必要量の倍ではありません。
>
> 今回の
>> test.Append(test.ToString());
> は丁度倍ですので、
> 確保の効率としては最適となっています。

なるほど、となると文字列変換した際の無駄もないので、
わりと効率的には動きそうですね。
いずれにしても再利用は出来なさそうなので、そうなると
300MBプラス300MBくらいになるのかな。
もちろんこの処理で使う分だけでの話ですが。

引用返信 編集キー/
■50547 / inTopicNo.8)  Re[4]: OutOfMemoryExceptionについて
□投稿者/ なちゃ (452回)-(2010/06/10(Thu) 16:04:01)
あと、StringBuilderの文字列変換では基本的にバッファがそのまま文字列に転用されたと思いますので、
文字列変換では意外と無駄は発生してない気がします。

引用返信 編集キー/
■50549 / inTopicNo.9)  Re[5]: OutOfMemoryExceptionについて
□投稿者/ マサヤ (8回)-(2010/06/10(Thu) 16:19:17)
http://msdn.microsoft.com/ja-jp/library/system.runtime.memoryfailpoint(VS.80).aspx
このやり方で、メモリをチェックしてあげれば OutOfMemoryExceptionは発生しなくなるんじゃないですか?
引用返信 編集キー/
■50558 / inTopicNo.10)  Re[6]: OutOfMemoryExceptionについて
□投稿者/ おそら (2回)-(2010/06/10(Thu) 17:30:17)
れい様、なちゃ様ご回答ありがとうございます。

  (れい様の発言より引用)
   >x86ならプロセスあたり2Gの制限があり、.NetやOSのDLLなどの分があるので
   >1GByteちょっとくらいしか使えません。

  (なちゃ様の発言より引用)
   >もちろんアプリケーションそのものが全部使える訳ではなく、
   >.NETアプリケーションの場合は目安として1GBくらいの感じです。

  お二人とも、目安が1G程度とのことですが、この値は状況により変動したりはしないのでしょうか。
  この1G程度の根拠となるページがあるのであれば教えていただければ幸いです。  


  (れい様の発言より引用)
   >> test.Append("");←適当に300文字くらい

   >適当じゃなくて一文字とか二文字とか、特定したほうが再現性がありますよ。

   失礼しました。今は272文字でやっています。
   WindowsフォームにてフォームのLoadイベントに最初のコードを記載したプロジェクトにて実験しています。


マサヤ様ご回答ありがとうございます。

 MemoryFailPointはどの程度のメモリが確保できるかを検知するものだと認識しています。
 例えばデータ量が可変の帳票を作成する時にかつ、サードパーティ製の製品等を使用した時に
 どの程度メモリが必要なのか分かるものなのでしょうか。

引用返信 編集キー/
■50569 / inTopicNo.11)  Re[7]: OutOfMemoryExceptionについて
□投稿者/ れい (931回)-(2010/06/11(Fri) 07:46:08)
No50558 (おそら さん) に返信
>   お二人とも、目安が1G程度とのことですが、この値は状況により変動したりはしないのでしょうか。

少ししか変化しません。
XPでもVistaでも7でも.Net1.1でも2.0でも3.5でも1GByteちょっとしか使えません。
安全をみて1GByte程度と私は考えています。

>   この1G程度の根拠となるページがあるのであれば教えていただければ幸いです。  

根拠は私の経験ですので、ページはありません。


引用返信 編集キー/
■50580 / inTopicNo.12)  Re[8]: OutOfMemoryExceptionについて
□投稿者/ おそら (3回)-(2010/06/11(Fri) 16:26:26)
No50569 (れい さん) に返信
> ■No50558 (おそら さん) に返信
>>  お二人とも、目安が1G程度とのことですが、この値は状況により変動したりはしないのでしょうか。
>
> 少ししか変化しません。
> XPでもVistaでも7でも.Net1.1でも2.0でも3.5でも1GByteちょっとしか使えません。
> 安全をみて1GByte程度と私は考えています。
>
>>  この1G程度の根拠となるページがあるのであれば教えていただければ幸いです。  
>
> 根拠は私の経験ですので、ページはありません。
>
>

回答ありがとうございました。
ちょっと考えて見ます。
解決済み
引用返信 編集キー/
■50593 / inTopicNo.13)  Re[7]: OutOfMemoryExceptionについて
□投稿者/ Jitta on the way (648回)-(2010/06/11(Fri) 19:47:24)
No50558 (おそら さん) に返信
>  例えばデータ量が可変の帳票を作成する時にかつ、サードパーティ製の製品等を使用した時に

サードパーティーの製品かどうかは、関係ないですね。標準添付のレポート ツールも、サードパーティー製です。


>  どの程度メモリが必要なのか分かるものなのでしょうか。

この、「必要なメモリ」が物理的なメモリをさしているなら、32ビット環境では4GB以上積んでも意味がありません。
アプリケーションがどれくらいのメモリを必要とするかという意味なら、アプリケーションに依存するので、ウェブの向こうにいる人にはわかりません。
使用するメモリを少なくする方法なら、あるいは返答できるかもしれません。例えば、1ページ印刷するのに必要なデータだけを保持するようにして、印刷要求を小分けにする、などです。データベースのデータを CSV 等のファイルにして、Word 等の「差し込み印刷」機能を使うという選択肢も考えられます。
解決済み
引用返信 編集キー/
■50693 / inTopicNo.14)  Re[8]: OutOfMemoryExceptionについて
□投稿者/ おそら (4回)-(2010/06/15(Tue) 08:59:37)
No50593 (Jitta on the way さん) に返信
> ■No50558 (おそら さん) に返信
>> 例えばデータ量が可変の帳票を作成する時にかつ、サードパーティ製の製品等を使用した時に
>
> サードパーティーの製品かどうかは、関係ないですね。標準添付のレポート ツールも、サードパーティー製です。
>
>
>> どの程度メモリが必要なのか分かるものなのでしょうか。
>
> この、「必要なメモリ」が物理的なメモリをさしているなら、32ビット環境では4GB以上積んでも意味がありません。
> アプリケーションがどれくらいのメモリを必要とするかという意味なら、アプリケーションに依存するので、ウェブの向こうにいる人にはわかりません。
> 使用するメモリを少なくする方法なら、あるいは返答できるかもしれません。例えば、1ページ印刷するのに必要なデータだけを保持するようにして、印刷要求を小分けにする、などです。データベースのデータを CSV 等のファイルにして、Word 等の「差し込み印刷」機能を使うという選択肢も考えられます。

Jitta on the way さん ご返答ありがとうございます。

 1つ目の返答についてはおっしゃる通りです。2つ目のものに対しては後者になります。

 MemoryFailPointは「実行前に十分なメモリ リソースがあるかどうかをチェックします」とMSDNにはありますが、
 これを使用する際にどの程度メモリが空いているかをチェックし、処理を行うためのトリガーとするのであれば、
 その処理にどの程度メモリーを使うか分かる必要があるのではないかと思ったためそのように書きました。

 そして、その例として帳票を挙げたまでです。それ以前に、メモリを節約しろってことですよね。
 そうするとこの「MemoryFailPoint」を使用する場面があまり思い浮かびませんが・・・・。
 メモリ操作が厳密に求められているようなプロジェクトもあるんでしょうね。

 メモリを節約するようにすると、実装者に依存することを心配しています。
 大枠では縛れるのですが、やはり実装者の個々の力量によりバラつきがでるのは仕方のないことなんでしょうか。

 ちょっと大きいプロジェクト(私が思うところで大きいだけかもしれませんが)の為、ちょっと心配です。
 最後はチラシの裏のようになってしまいましたが、ありがとうございました。

解決済み
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -