こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

配列を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

投稿日時 - 2013-07-28 22:39:40

QNo.8196200

すぐに回答ほしいです

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

使用メモリが断片化して、2970万個分の連続領域が確保できない、というケースもありそうです。

初期化ですが、Excel再起動以外の方法は、私は知りません。

投稿日時 - 2013-07-29 14:45:54

補足

そうですか・・・断片化と聞いて勝手に想像してみたのですが
"a"がアドレス一個分のメモリを消費
"aaaaaaa~a"がアドレス100個分のメモリを消費すると仮定した場合
最初に
a(0):001番地
a(1):002番地
a(2):003番地

といった具合だったのが
a(0):001~100番値
a(1):101~200番値
a(2):201~300番値

といった感じで、メモリのアドレスを占有するけれど
次に、"a"で格納しようとした場合、実際に使うのは1個分のアドレスだけど
a(0):001番値、002~100番地はデータのゴミ
a(1):101番値、102~200番地はデータのゴミ
a(2):201番値、202~300番地はデータのゴミ

という感じになって、002~100番地を有効に使えないようなイメージでしょうか。
どうにか解決策が知りたいです。

投稿日時 - 2013-07-29 20:02:37

ANo.2

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(8)

ANo.8

VBAからEnviron関数で取得してみて設定出来ているのであれば
(おっしゃるとおり)無理なのでしょうね。

投稿日時 - 2013-07-31 07:01:06

補足

Environ("OANOCACHE")の値は1になっているので、設定はできているみたいです。
どうもありがとうございました

投稿日時 - 2013-07-31 23:03:59

ANo.7

>そうなると、なるべく配列は減らして、影響を少なくなるように設計するしかなさそうですねぇ・・・

配列の上限を戻す方法はわかりませんが、上限が大きな配列は1つしか使用しないという事であれば最初から最大分確保しておいてそれを使い回すとか(上限値は別途管理しないといけませんが)、複数の配列をまとめて1つの配列のように扱うクラスを作るとかいう手もあるかと思います。

投稿日時 - 2013-07-31 02:03:15

補足

なるほど・・・
とりあえず、プログラムの方で工夫してみようと思います
ありがとうございました

投稿日時 - 2013-07-31 23:04:53

ANo.6

環境変数の設定の仕方

参考URL:http://www.atmarkit.co.jp/fwin2k/operation/wsh06/wsh06_02.html

投稿日時 - 2013-07-30 08:08:27

ANo.5

> BSTRキャッシュを無効というのは、String型以外にしろということでしょうか?
(用語の意味が分からないなら)ググらないと。

参考URL:http://keicode.com/com/bstr-cache.php

投稿日時 - 2013-07-30 06:47:48

補足

すみません。ご親切にありがとうございます。

システム環境変数にOANOCACHE=1を追加して、PCを再起動してみましたが、特に変化は見られませんでした。

やはり、無理なのでしょうか

投稿日時 - 2013-07-30 20:55:05

ANo.4

>そうですか、では、配列の上限値をもとの状態に戻すことは不可能なのでしょうか?

申し訳ありませんが可能かどうか私にはわかりません。

#2の方がいわれるように断片化が原因であるなら再起動するしか手はないと思いますが。

投稿日時 - 2013-07-29 19:47:18

補足

なるほど、断片化が原因なら無理なんですね。
どうも、それっぽい感じもしますね。
NO2でちょっと勝手ながら断片化の解釈しちゃってますけど
あれだと、配列が同じ数だけしか作れない事の
つじつまがあいますし、Excelの再起動で直るわけですし。

そうなると、なるべく配列は減らして、影響を少なくなるように設計するしかなさそうですねぇ・・・

毎回マクロ実行するたびにexcel起動しなおさなければならないのもあれですが・・・

投稿日時 - 2013-07-30 23:08:39

ANo.3

単にOSのキャッシュが効いてるだけでは。
BSTRキャッシュを無効にしてみるとか。

※ BSTR(VBAのString型の事)

投稿日時 - 2013-07-29 16:46:35

補足

BSTRキャッシュを無効というのは、String型以外にしろということでしょうか?

残念ながら、扱う値は文字列なのです。
確かに、long型とかだと、値変えてもあまり変化ないですね。
string型だと可変長の長さを持っている為、
一度大きな文字列を入れてしまうと、文字列を後から入れなおしても
メモリの番地を詰めることができないということなんでしょうか・・・
文字列だと不可能なんですかね

投稿日時 - 2013-07-29 20:00:59

ANo.1

>また、添付した絵は、実行した時のタスクマネージャーの様子です。

VBAの場合はどうかは知りませんが
プログラム上でメモリを解放=OSにメモリを返す
とは限りません(たぶん返してないと思いますけど)。

投稿日時 - 2013-07-28 23:53:02

補足

そうですか、では、配列の上限値をもとの状態に戻すことは不可能なのでしょうか?
そこの部分が知りたいところです。
よろしくお願い致します。

投稿日時 - 2013-07-29 08:38:09

あなたにオススメの質問