tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルメモリー不足について
記事No4277
投稿日: 2006/09/20(Wed) 12:16
投稿者VB2005凡迎徹
いつも参考にさせてもらってます。
現在、テキストファイルを読み込んで、中から必要な内容を
抽出するソフトをVB2005で作っています。
PCの環境は、OS:Win2000(SP4)、CPU:2.6GHz、RAM:2GB、HDD:80GBです。
このソフトを作るに当たり、読み込むテキストファイルが大きいためにRAMを増やしました。
ところがプログラムを走らせてすぐにテキストファイルを「ReadAllText」で
読ませようとしたところ、200MBのファイルを読み込むと
「OutOfMemoryExceptionの例外がスローされました」というエラーがでて
読み込むことができません(150MBくらいまでなら読み込めます)。
タスクマネージャーを見ても使用可能領域は十分あります。
読み込めない原因が、VBなのかハードなのかOSなのかわからず途方に暮れています。
どなたか解決策をご存知の方がいましたらご教授お願いします。
(解決のためのヒントでも結構ですのでお願いします。)

[ツリー表示へ]
タイトルRe: メモリー不足について
記事No4278
投稿日: 2006/09/20(Wed) 14:36
投稿者ヤマ@文系
仕様だと思います。
readでoffsetをずらして分割して読み込むとか、配列にしてみたりするといいかもしれません。
結局byte配列にしてもstringにエンコードしてもnothingになると思いますが。。
続きはちょっと調べて書きます。

以前書いたコードが残っているかもしれないので。

[ツリー表示へ]
タイトルRe^2: メモリー不足について
記事No4280
投稿日: 2006/09/20(Wed) 16:17
投稿者ヤマ@文系
integerになってませんか?
string自体charのコレクションなので
streamreader?などで扱えないのではないでしょうか?

Char()を使うのも手だと思います。
私の場合面倒なのでFileOpenを使ってLineInputを使っていたようです。

[ツリー表示へ]
タイトルRe^3: メモリー不足について
記事No4281
投稿日: 2006/09/20(Wed) 16:18
投稿者ヤマ@文系
streamreaderで扱えるのが。<=抜けてました。
> integerになってませんか?

[ツリー表示へ]
タイトルRe^4: メモリー不足について
記事No4282
投稿日: 2006/09/20(Wed) 17:36
投稿者VB2005凡迎徹
ヤマ@文系さん、ありがとうございます。
すみませんが「streamreaderがstringを扱えないのでは?」
の点がよく理解できませんでした。ヘルプドキュメントでは
streamreaderで読み込んだものをstringに格納しているように
書いてあるのですが・・・。(初心者なもので、せっかくの
アドバイスを理解できずすみません。もう少し説明をお願いします)
それから読み込みファイルの最初の方と最後の方を何度も
行き来して抽出を行いますので、一行ずつ読み込む方法だと
プログラムが複雑になるのと時間がかかると思い、一気に読み込む
「ReadAllText」を使うようにしました。ですのでできれば一気に
読み込む方法が使えるような対策をお願いします。
それから、メモリの使用可能領域が十分あるのに、エラーが発生する
理由などについてご存知でしたら教えてください。
教えてもらう身でありながら、お願いばかりですみませんがよろしくお願いします。

[ツリー表示へ]
タイトルRe^5: メモリー不足について
記事No4283
投稿日: 2006/09/20(Wed) 19:35
投稿者ヤマ@文系
 
> すみませんが「streamreaderがstringを扱えないのでは?」
stringがcharの配列というかコレクションなので、charの要素数がintegerのサイズを超えると
StreamReaderというかread系では扱えないのではないかなぁという意味です。
ヘルプよく確認していないのですが。調べてみてください。
同じところではまった記憶があります。


> 「ReadAllText」を使うようにしました。ですのでできれば一気に
> 読み込む方法が使えるような対策をお願いします。
分割して
readメソッドを使ってオフセットをずらしていくなどはどうですか。
一気に読み込むTIPSは探したけれどもなかったので、
バイナリで読み込んで、コンバートとか。。
(これもエンコーディングクラスで失敗する可能性あり)
参考になるようなサイトちょっと忙しくて探せないのですが。。あると思います。

> それから、メモリの使用可能領域が十分あるのに、エラーが発生する
> 理由などについてご存知でしたら教えてください。

readalltextで確保されている領域(int32(integer))をファイルサイズが超えているという意味ではないですか?サイズを変えて読み込ませてみてください。。。

[ツリー表示へ]
タイトルRe^6: メモリー不足について
記事No4286
投稿日: 2006/09/21(Thu) 10:49
投稿者魔界の仮面弁士
# 巨大ファイルを用意して検証するほどの余裕が無いので、以下、未検証で書いています。m(_ _)m

≫ヤマ@文系さん

>> stringがcharの配列というかコレクションなので、
アンマネージの話をしているのなら別ですが、少なくともマネージコードにおいて、
.NET の String(≠string) が Char(≠char)配列であるという事は無いのでは。

もちろん、『[String].ToCharArray()』や『New String([Char配列])』を使う事で、
String と Char配列を相互に変換する事は可能ですが、それでも両者は別物であり、
  Dim A As Object = New Char() {"X"c}
  Dim B As Object = TryCast(A, String)
のようなコードにおいて、B は Nothing になってしまうことでしょう。


>> charの要素数がintegerのサイズを超えると
「integerのサイズを超える」というのは、具体的にはどういう意味でしょうか?

今回の OutOfMemoryException の件では、200MB のファイルということでしたので、
単純なバイト換算で、 209,715,200 バイトという事ですよね。

一方、Integer型 (System.Int32) のマネージサイズは 32 bit(4 バイト)、
その最大値は 2,147,483,647 ですので「サイズを超え」てはいないと思いますが…。



≫VB2005凡迎徹さん

>>>>>> 読み込むテキストファイルが大きいためにRAMを増やしました。

たとえば下記では、(読込ではなく書込ですが) 198MB を境として OutOfMemoryException が
発生したという報告がありますが、他の開発者の環境では再現させる事ができなかったようです。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=30540&forum=7

問題点を明らかにするためにも、まずはその例外が、環境依存/データ依存/クラス依存の
いずれによって齎されているのか、切り分けが必要かと思います。


1. 別の環境で試してみて、同じエラーになるのか?
 (環境への依存性の検証。OS の違い、.NET Framework の違いなど。)

2. たとえば、半角空白が並んでいるような単純なデータを読み込む場合にも、
 同じサイズで例外が発生するのか?
 (データ依存性の検証。確実に読めるデータの繰り返しでチェックする。)

3. 例外になるファイルサイズは一定か? それとも数バイト以上の誤差があるのか?
 (データ依存性の検証。150MB まで OK というなら、次は 175MB で検証するなど、
 段々と範囲を狭めていって、現象を起こす分岐点を探す。)

4. 文字列としてではなく、バイナリとして Byte配列に取り込んだ場合はどうか?
 (クラス依存性の検証。この場合は、メモリ確保に失敗したのか、それとも
  文字コード変換時のエラーなのかを判断しています。)

5. Stream からの一括取得ではなく、部分的な取得であれば問題は起きないのか?
 (もしこれで問題が無いなら、String に対する処理部分を、Stream に対する処理に
  書き換えてやれば、問題を解決できる可能性が高まりますよね。)


>> プログラムが複雑になるのと時間がかかると思い、
データサイズが大きい場合には、大量のメモリを一度に確保するという行為は、
長い時間を必要とする可能性があります。(メモリ解放にも時間がかかります)

また、連続した巨大なメモリ領域を確保しようとした場合には、一度に大きな領域を
確保できず、メモリ不足例外となる可能性が高まる点に注意してください。
http://forums.microsoft.com/msdn-ja/ShowPost.aspx?PostID=227022&SiteID=7

元のファイルのデータ形式が不明なので、単純には判断することができませんが、
巨大なデータ(たとえば数十メガバイトを超えるファイル)であるならば、
一括読込はできるかぎり避けて、逐次読込のための仕組みを検討した方が安全でしょう。

# これが Byte配列や String ではなく、Stream や StringBuilder であれば、
# メモリの持ち方が異なるでしょうから、また結果が変わってくるかも知れません。


>>>>>> タスクマネージャーを見ても使用可能領域は十分あります。
>> メモリの使用可能領域が十分あるのに、
確認するとすれば、タスクマネージャーではなく、パフォーマンスモニタですね。
(ファイル名は、「perfmon.msc」です)
パフォーマンスモニタの .NET CLR Memory オブジェクトをチェックしてみてください。

[ツリー表示へ]
タイトルRe^7: メモリー不足について
記事No4292
投稿日: 2006/09/21(Thu) 18:44
投稿者VB2005凡迎徹
魔界の仮面弁士さん、ヤマ@文系さんありがとうございます。
今現在の私のスキルで出来ることとして、一行ずつ追加方式で読み込んで
全体を読み終わったものをもとに抽出を行う方法で進めようと考えています。
一行ずつ読み込む方式については、すでにアドバイスを頂いていたのですが
時間がかかると思っていたのと、追加方式を知らなかった、という理由から
一気に読み込む方式にこだわっていました。アドバイスを頂いて調べたところ
遅まきながら追加方式を知りました。また、この方法で読み込んでも200MBくらいなら
1minもかからないことがわかり、この方法を使用しようかと考えています。
ただ、魔界の仮面弁士さんのアドバイスについては、出来る限り調べて見たいと思います。
今の私の環境やレベルでは難しいこともいくつかあるのですが、自分の勉強にもなりますので
できるだけ挑戦するつもりです。皆様色々とご指導頂きありがとうございました。
今度もよろしくお願いします。

[ツリー表示へ]
タイトルRe^8: メモリー不足について
記事No4295
投稿日: 2006/09/21(Thu) 21:21
投稿者ヤマ@文系
LineInputでやるなど
ある程度の間隔でapplication.doevents()
(2005?では別スレッドでやってもできます)
を行うとよいですよ。。(って知ってますね)
役に立たなくてすいません。(--;

[ツリー表示へ]
タイトルRe^7: メモリー不足について
記事No4294
投稿日: 2006/09/21(Thu) 20:32
投稿者ヤマ@文系
>>魔界の仮面弁士様
ありがとうございます。勉強させていただきました。
もう少し.NET自体勉強していきたいとおもいます。
レスありがとうございました。m(_ _)m
当方の環境でも起きるので、.NETかなにかの仕様と思っていたのですが、
やはり、一括してメモリ領域確保すると発生するようですね。

私の場合は以前、分割して読み込んで逃げた記憶があります。

5つの点で、思い当たることだけでも書いておきます。。
4. 文字列としてではなく、バイナリとして Byte配列に取り込んだ場合はどうか?
byte配列で読み込む手法は使えると思います。
ただ、一括してエンコーディングクラスなどでエンコードするとやはりNothingが帰ってきます。

5. Stream からの一括取得ではなく、部分的な取得であれば問題は起きないのか?
Readメソッドでオフセットをずらしながらの取得であれば問題は起きなかったと思います。

[ツリー表示へ]
タイトルRe: メモリー不足について
記事No4296
投稿日: 2006/09/21(Thu) 22:35
投稿者花ちゃん
ReadAllText に拘らないのなら、下記の方法で取得できます。

http://hanatyan.sakura.ne.jp/dotnet/fileio01.htm

上記の1の方法では、約30秒 かかり、2の方法では、12秒程度で取得できます。
(Celeron 2.4GHz   496 MB RAM WinXP VB2005)

但し、2の方法のバッファを確保している部分は、限度一杯のファイルを想定しており
ませんので、 CInt 及び有効メモリの範囲内になるように注意して下さい。

# ReadAllText で取得の場合はやはり、エラーが発生します、何らかの制限が加えられて
 いるのでしょうかね。

[ツリー表示へ]
タイトルRe^2: メモリー不足について
記事No4309
投稿日: 2006/09/24(Sun) 00:15
投稿者ヤマ@文系
> http://hanatyan.sakura.ne.jp/dotnet/fileio01.htm

文字列を結合するのに & を使ってループは遅いだろうと思って
stringbuilderというものがあるのをしってappendしてやっていたのですが
stringbuilderクラス自体読み込むときに使うっていう発想が抜けていました。(^^;
かなり早いものなんですね。勉強になります。
.net で LineInputでもそこまで遅くはならないと思いましたが、、
ちょっとstringbuilderとどれくらい変わるかやってみたいと思います。

> # ReadAllText で取得の場合はやはり、エラーが発生します、何らかの制限が加えられて
>  いるのでしょうかね。
readtoendもencodingクラスもエラー(またはnothingがかえる)がでますね。。ファイルサイズがでかいと・・

[ツリー表示へ]