• ベストアンサー

Redimした動的配列はEraseする必要があるのか

Visual Basic6(SP5)で開発を行っているものです。 時々、「メモリが"Read"になることができませんでした」と言うエラーメッセージと共にVisual Basicのアプリケーションが終了する不具合が発生します。メモリ操作関連のエラーだろうと社内で指摘されました。 怪しいかと思われるのは、ある動的配列をRedimし、解放する必要が無いのでEraseしていない点です。 動的配列は、RedimしたらEraseしないとエラーの元になり得るのでしょうか? 時間のある方、ご回答願います。

質問者が選んだベストアンサー

  • ベストアンサー
  • gungnir7
  • ベストアンサー率43% (1124/2579)
回答No.3

多分、直接的には関係ないと思われます。 ポインタをまともに扱うことのないVBでは 領域侵害などが発生する機会が少ないからです。 過去に相当大きな空間(200M)をredimで確保したままにしたことがありますが、 ご質問のエラーに遭遇したことはありません。 もし疑うのであれば、memorycopy、Bitbltなどの メモリ転送APIの方がよほどたちが悪いです。 redimの場合はOSが勝手にアドレス空間を確保してくれるはずなので、 それができていないとすれば重大な障害がある感じがします。 マシンに依存する問題の可能性も捨て切れません。 社内ということなので、この辺りの問題は切り分け済みでしょうが、 参考までに挙げてみました。

kagiyo
質問者

お礼

回答、ありがとうございます。 No2の方にも指摘されましたが、APIを利用したメモリ転送を行っています。 そして思い起こせば、質問のエラーが起こり始めたのは、そのAPI関数の実行を阻害する可能性のある処理を追加した頃からです。 そのあたりを調べてみます。

その他の回答 (2)

  • KenKen_SP
  • ベストアンサー率62% (785/1258)
回答No.2

> 動的配列は、RedimしたらEraseしないとエラーの元になり得るので > しょうか? エラーには必ず原因があるわけですが、原因解明にできるだけ手間を かけないようにするためにも動的配列やオブジェクト変数などは、 使い終わった時点で私は明示的にクリアしています。 広域変数もなるべく使わないようにし、使う前に必ず明示的に初期化 し、使い終わったらクリアしています。 >「メモリが"Read"になることができませんでした」 意味不明のエラーメッセージですが、意図するところは、  「参照したメモリの内容を読み取ることができませんでした」 です。動的配列がこのエラーの原因となり得るのかはコードを見ない と何とも言えませんが、よくあるのは API 絡みですね。宣言ミスと か使い方のミスとか。もし、API を使っているなら確認してみて下さ い。

kagiyo
質問者

お礼

ありがとうございました。 今さらになって思うのは、「怪しい」と私自身が思っているなら明示的にEraseしておけばよかっただけかも知れません。 APIは確かに使っています。

  • xdaix
  • ベストアンサー率66% (2/3)
回答No.1

広域変数だとメモリに残ってしまうことがあるので (といってもExcel VBAですが) 自分はすべてEraseするクセをつけてます。

kagiyo
質問者

お礼

ありがとうございます。 「広域変数だとメモリに残ってしまうことがある」時点で、確実にEraseする必要がありそうですね。

関連するQ&A

  • 配列をEraseしてもメモリが開放されていない?

    すみません。一度質問を載せた後で、補足で絵を載せようとしたのですができなかったので 再投稿させていただきます。(元の質問はけしました。) -------------------------------------- こんにちは、今作成しているエクセルのVBAで配列を膨大に食ってしまい、メモリ不足に落ちいるという現象に悩まされています。 そのため、なるべくいらなくなった配列はEraseステートメントを使って解放しようとしてるのですが、 これが上手くいっている気がしません。 ためしにフォームと、ラベルを1つずつ用意し、下記のような配列をどこまで作れるか計算するプログラムを組んでみました。 1回目、ans(i) に"a"というaを1個のみ格納した場合で計算すると、配列は2970万個できます。 2回目、ans(i) に"aaa~a"というaを64個格納した場合で計算すると、配列は770万個できます。 まぁ、ここまでは当然の結果だと思います。 可変長のString型変数の場合は、割り当てられるメモリサイズが固定ではないらしいので (前回の質問で教えてもらいました) しかし3回目、ここでもう一回、1回目と同じ、"a"を一個のみ格納した場合で計算してみます。 私の予想では1回目と同じ2970万個程度の配列が作れると思っていました。 なぜなら、Eraseステートメントでメモリを解放していたし、 タスクマネージャーのPF使用量(おそらくメモリの値)を見ても2回目を実行した後の値は 1回目前と同じ値に戻っているからです。 しかし、実際試してみると、 2回目と同じ770万個の配列しかつくれませんでした。 つまり、Eraseステートメントを使用しても配列の上限値は元の状態に戻らなかったのです。 どうすれば、配列を再び2970万個作れる初期の状態に戻せるのでしょうか? 現時点ではエクセルを起動しなおせば、元に戻りますが、それでは困ります (プログラムの途中で配列の数を減らしたり、コピーしたりを行っている為、 どうしてもプログラム中でメモリの解放を行いたいからです) また、添付した絵は、実行した時のタスクマネージャーの様子です。 参考になれば幸いです どうか、みなさんお知恵をお貸ししてください --------------------------------------------------- '実行するときは、フォームとラベルを1つずつ用意して実行してください Public Sub 配列上限取得計算() On Error GoTo ErrEnd Dim i As Long Const kankaku As Long = 100000 Dim ans() As String ReDim ans(1 To kankaku) As String ans(1) = 1 i = 2 UserForm1.Show vbModeless Do If i Mod kankaku = 0 Then DoEvents UserForm1.Label1 = i ReDim Preserve ans(1 To i + kankaku) As String End If '最初にaを1個のみ格納した場合は、2970万個まで配列が作れるが、次にaを64個格納した場合は770万個まで作れた。 'しかし、その直後にまた、aを1個のみ格納した場合で実行してみると、770万個しか作れない 'つまり、Eraseステートメントを使っているにも関わらず、同じ条件のプログラムでも配列の上限が下がってしまう。 'この現象をなんとか回避したい。元の状態にリセットするにはエクセルを起動しなおさないと直らない。 '1回目と3回目に実行するコード、2回目はコメントアウトしてください ans(i) = "a" '2回目に実行したコード、2回目はコメントアウトを解除してください。 ' ans(i) = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" i = i + 1 Loop Erase ans Unload UserForm1 Exit Sub ErrEnd: Erase ans MsgBox "これ以上の配列を設定できません。" & vbCrLf & "上限は" & i & "です。" & vbCrLf & Err.Description Unload UserForm1 End Sub

  • ReDim PreserveよりもReDimが遅い

    こんにちは、配列がいくつまで取得できるのか計算するマクロを作っている時に変なことに気が付きました。 エクセルでフォームとラベルを1つ用意し、下記のようなプログラムをモジュールに書きます。 実行すると、10万ずつ数字が増えていき、数字が1000~2000万超えたあたりで、メモリ不足ですという 表示がでます。 ここで、ReDim PreserveのPreserveをとって、ReDimだけにすると、数字の増え方が目に見えて遅くなります。 ReDim Preserveとしたときは9.5秒ぐらいですが ReDim の時は 30秒以上かかっています 3台のマシンでテストしましたが、どれも似たような結果になりました 普通に値を保持するredim Preserveの方が遅いと思うのですがなぜこのような結果になるのでしょうか? ご教授お願いいたします。 Public Sub 配列上限取得計算() On Error GoTo ErrEnd Dim i As Long Const kankaku As Long = 100000 ReDim ans(1 To kankaku) As String ans(1) = 1 i = 2 UserForm1.Show vbModeless Do If i Mod kankaku = 0 Then DoEvents UserForm1.Label1 = i 'ここのPreserveをなくすと明らかに遅くなる ReDim Preserve ans(1 To i + kankaku) As String End If ans(i) = i i = i + 1 Loop Erase ans Unload UserForm1 Exit Sub ErrEnd: MsgBox "これ以上の配列を設定できません。" & vbCrLf & "上限は" & i & "です。" & vbCrLf & Err.Description Erase ans Unload UserForm1 End Sub

  • メモリの解放について VB6 VBA

    VB6やVBAで動的配列をERASEしたのですが、タスクマネージャーで見ても使っているメモリを解放しているように見えません。 動的配列の内容をMsgBoxで表示させるたびにメモリがどんどん減っていきます。Eraseしても戻りません。 Redim ArryaDat(0)とかでも無理でした。 APIを使って(どんな方法)でも、メモリを解放したいのですが、可能でしょうか? Dim ArrayDat() as String Redim ArrayDat(100) ArrayDat(0) = "なんとか" ArrayDat(1) = "かんとか" ... ArrayDat(100) = "メモリを解放したい" for i = LBound(ArrayDat) to UBound(ArrayDat) MsgBox("どんどんメモリが消費されていく・・ [" & ArrayDat(i) & "]") next i Redim ArrayDat(0) Erase ArrayDat NsgBox("解放したつもり? 誰か教えて") あと、.NETではメモリ解放はどうなっているのでしょうか? まだ使いませんが、頭の片隅に入れておきたいです。

  • VB.netでの動的配列の扱い

    VB.net 2003で開発を行っています。 コード内で動的配列を多用しているんですが(要素数 2000000以上)、一度 ReDim でメモリ確保した配列は、いらなくなったと同時にErase したほうがいいんでしょうか。 配列を増やすほど実行速度が遅くなっていく気がします。 また、いっそのことVC++で書き直すと、実行速度は速くなる可能性はありますか。 VB.netのコードは、もちろん最適化のチェックをすべていれてあります。 どなたかご教授下さい。

  • VBA 停止したら配列は開放されていますか?

     質問させていただきます。よろしくお願いいたします。 環境:Windows7 + エクセル2010 でございます。 少し気になりましたのでご質問させてください。  50MBほどのエクセルファイルでデータを加工するために、大きな配列を下記のように繰り返し使用する事がよくございます。 Do  ReDim varData(1000000, 10)   :   : ★  Erase varData Loop Until ~  Erase前(例えば★マークのところ)でエラーやブレークによる中断 →そのままVBAを「停止」(=四角いリセットボタンの事です) →デバッグしてからVBA再実行 を何度も繰り返しておりますが、「停止」させた時点でこの配列のメモリは開放されているのでしょうか?  といいますのが、何度もVBA実行→中断を繰り返しておりますとエクセルの動作が重くなってくる事があるので、上記の問題を疑ってエクセルを一度落として再起動することが時々あります。 (再起動すればメモリが残っていても開放されると思いますので。 しかしエクセルファイル自体が重いので、何が原因なのかは不明です。)  毎回きちんとEraseできればメモリは開放されると思うのですが。。。  あともしメモリ上に残っているのであればですが、VBA起動直後にそれを確認してEraseするようなコードがもしございましたらお教えいただけないでしょうか。 試しに Sub Test  If varData<> Empty Then Erase varData のような事をしてみましたが、 >実行時エラー13:型が一致しません のエラーが出てうまくいきませんでした。  もしお詳しい方がいらっしゃいましたら、どうぞよろしくお願いいたします。

  • Excelのメモリ(配列)の上限は2Gではないのか

    こんにちは、 現在ExcelのVBAで大量に配列を必要なマクロを作成しています。 その為、計算量が増えるとどうしても、 メモリ不足というエラーが発生してしまい困っています。 そこで、現在使っているExcelがどれだけの配列とメモリを使用できるのか 下記コードを使用してテストしてみました。 そうすると、下記のような値の時メモリ不足というエラーが発生してマクロが終了しました (下図参照) ・メモリ(プライベートワーキングセット)  :1249716 K (約1G?) ・String型配列数(各要素"01,02,03,04,05,06") :約28000000(2800万個) 私の知識では、32bitアプリケーションのメモリの上限は2Gだと聞いています。 ですが、実際にはその半分しか使われていません。 そこで質問となるのですが ・32bitアプリケーションの上限が2Gと言われているのはプラベートワーキングセットの値のことではないのか? ・32bit版Excelを使用して、これ以上のメモリ(配列)を使用することは可能か ・可能であれば、その方法はどんな方法か? 以上のことについてお聞きしたいと思っております。 上のどれか一つでもいいです。知っていることがあれば教えてください。 補足となりますが、テストしたPCの簡単な環境を下に記載して置きます。 どのPCでも上記結果とほぼ変わりはありませんでした。 PC1 Windows7 32bit メモリ 4G Excel2013(32bit) PC2 Windows7 64bit メモリ 8G Excel2010(32bit) 以下は使用したプログラムコードです --------------------------------------------------------------- Public Sub 配列上限取得計算() On Error GoTo ErrEnd Dim i As Long Const kankaku As Long = 1000000 Dim Moji As String Moji = "01,02,03,04" Dim ans() As String ReDim ans(1 To kankaku) As String i = 1 Do If i Mod kankaku = 0 Then ReDim Preserve ans(1 To i + kankaku) As String End If ans(i) = Moji i = i + 1 Loop Erase ans Exit Sub ErrEnd: MsgBox Err.Description & vbCrLf & "これ以上の配列を設定できません。" & vbCrLf & "上限は" & i & "です。" Erase ans Err.Clear End Sub

  • 二次元配列でソートがしたい

    二次元配列でソートがしたい 二次元配列でソートがしたい 開発環境:Visual Web Developer 2008 express 言語:Visual Basic 上記開発環境でWebアプリケーションを開発中です。 TextBoxを縦20行横10列の表形式に配置し、左から2番目の列に入力された数値をキーに、昇順に並べ替えができるような入力フォームを作成していますが、参考になるようなサイトが見つからなかったので、どなたか参考になるサイトやサンプルロジックを教えて頂けないでしょうか? よろしくお願いします。

  • 配列の中の最大値とそのインデックス番号を取得する方法

    Visual Basicを学んでまだ10日です。 いま、配列中の最大値と、そのインデックス番号を取得する方法を考えています。 なるべく計算処理に時間が掛からない方法で行いたいのですが、なにか良い方法はありますか? For j = 0 To 100 For p = 0 To 415 scan1(p) = Sheets("Sheet1").Cells(j, p + 2) scan2(p) = Sheets("Sheet2").Cells(j, p + 2) Next p correlation(j) = Application.WorksheetFunction.Correl(scan1(), scan2()) Erase scan1 Erase scan2 Next j で、この後に correlation(j)配列の最大値とそのインデックスを求めたいです。 よろしくお願いします。 ちなみに、配列の中の値は、重複を含んでいません。

  • Vector定義の配列の共有メモリ化

    Vectorの1次元配列(以下配列Vとします)を共有メモリとして、複数のアプリケーションで利用する方法がわかりません。開発ツールはVC++6.0 SP6です。 配列Vの値を参照時、書込時にはポインタ渡し(memcpy)をしたいです。コンパイルは通るのですが、アプリケーションを実行時にmemcpyを実行時に落ちます。 以下のことを確認しています。 (1)resizeでサイズは同じにしています。 (2)変数自体はNULLではありませんでした(デバッカで確認すると、アドレスがありました。) (3)変数の定義時にVectorにせず、サイズ決めた配列にするとポインタ渡しはできています。パラメータファイルを参照し、配列のサイズを決めたいので、ソース上に記入するのはできません。 よろしくお願いします。

  • 二次元配列について

    二次元配列が確保(?)されてなくて困っています。 プログラム内で、以下のように宣言するのですが、 #define MM 1025 //プログラム6行目 #define NN 10 //7行目 double y[MM][NN]; //17行目 y[0][0] = 3.0; //28行目 Visual Studio.NET のデバッグツールの「ウォッチ」で、 y[0][0]に代入された値を確認しようとすると、 エラー:インデックス'0'は、ポインタ/配列'y'の範囲外にあります と、表示され、中身を確認することができません。 それどころか、プログラムの後半98行目を境に、 y[0][0]の値が上書きされてしまいます。 x[k] = x[0] + k*h; //98行目 ここから、察するにメモリがちゃんと確保されていないと、 思うのですが、どうすればいいかわからず、困っています。 どなたかご教授をお願いいたします。 環境 OS : Windows XP 開発環境 : Visual Studio.NET

専門家に質問してみよう