ホーム ] TIPS ] ソフトウェア実験室 ]

上へ
ビットマップの処理速度
色変換速度
数式演算速度
冪乗演算速度
検索速度
文字列処理速度
文字列/数値処理速度
CPU演算速度
TicksとPerformance Counter
文字の数値化
数値化文字の再現
数値化文字の補間
補間の効果
ネイピア数
ネイピア数2
指数関数近似値
級数の収束速度1
級数の収束速度2
級数の精度
逆三角関数を求める
算術幾何平均でπを求める
全フォルダ列挙
ビットマップとメモリリソース
配列とメモリリソース

ソフトウェア実験室

ビットマップとメモリリソース

最終更新:2006/10/18 修正

 .NET では、ガベージコレクションが自動的に行われ、下手なプログラムでも滅多にメモリリークは起こらない。しかし、この危険性は身近に潜んでいる。ここでは、ビットマップ処理を通じでメモリ不足を起こし、システムの挙動を観察する。

●予備知識

 ビットマップなどインスタンスが生成されるクラス(オブジェクト)は、全て参照モードで、

 Dim bmp As Bitmap

では、bmp が Bitmap を示す入れ物であるとの定義のみで実体はない、しかし、

  Dim bmp As New Bitmap(・・・・・・・・・・・・・・・・)
 Dim bmp As Bitmap = Bitmap.FromFile(xxxxxxx)

などとすれば、bmp には、ビットマップのインスタンス(実体でありメモリ上に展開された意味のあるデータ群)へのポインターが格納される。ポインターとはインスタンスを記述したディバイスコンテキストのハンドルの場所をしめすアドレスの一種。

 この状態で問題となるのはインスタンスであり、メモリを食う実体である。通り一遍の処理ならば、特に解放しなくとも、そのアプリが終了した後、暫くの後に自動的に解放される。これは、.NET が、誰からも参照されないオブジェクトを見つけ掃除を行う(ガベージコレクション)からである。

 しかし、アプリが実行されている間には、暗黙的な解放は行われない。従って、長大なループ処理内では明示的なオブジェクトの解放が重要となる。正規のオブジェクト寿命内であっても、アプリにとって不要なものは、明示的に解放する必要がある。

→ちなみに、GCクラスを用いれば、ガベージコレクションを強制できる。GC.Collect()

●ビットマップで破綻を起こす

○破綻例1

Dim bmp As Bitmap
Dim FP() As String = 沢山のイメージへのパス

For i = 0 to 沢山
   bmp = Bitmap.FromFile(FP(i))
   (そのビットマップの処理)

Next
bmp.Dispose

ループ終了後にDispose しているが、この例では後の祭りである。ループ中の、Bitmap.FromFile で、それぞれインスタンスが生成され、メモリに積上げられている。やがて、システムがピクピクしだし、画面が固まってくるし、ディスクがやたらに動き出す。ひ弱なシステムでは、”メモリ不足です”と言って、死んでしまう。また、最後にあるDispose では、最後に参照したインスタンスのみ解放される。

○破綻例2

 ピクチャーボックスのバックグランドイメージでも起こる。

Dim FP() As String = 沢山のイメージへのパス

For i = 0 to 沢山
   PictureBox.BackgroundImage = Bitmap.FromFile(FP(i))  
   (その他の処理)

Next

 ピクチャボックスのバックグランドイメージに動的にピクチャを割り当てているが、BackgroundImage は、内部にイメージを保有している訳ではなく、外部を参照しているので、1 と同じことが起こる。

●回避策

ループ内で、不要になったら解放する。

For i = 0 to 沢山
   bmp = Bitmap.FromFile(FP(i))
   (そのビットマップの処理)
   bmp.Dispose

Next

この例では、bmp は、Bitmap.FromFile(FP(i)) と同じインスタンスを示しているので、bmp.Dispose は、結局、Bitmap.FromFile(FP(i)) で生成されたインスタンスを破棄することと同じとなる。

バックグランドイメージでは、

For i = 0 to 沢山
   If Not (PictureBox.BackgroundImage Is Nothing) Then PictureBox.BackgroundImage.Dispose
   PictureBox.BackgroundImage = Bitmap.FromFile(FP(i))  
   (その他の処理)

Next

などとすれば良い。これも上記と同じで、BackgroundImage は、Bitmap.FromFile(FP(i)) と同じインスタンスを示している。ピクチャボックスでは表示するのが目的なので、新しい画像を参照する直前に以前のものを解放するのが良い(表示画像の連続性確保)。

●実験

 以上の二通りについて破綻と回避を実験した。メモリの状態は、システムオブジェクトである

  Environment.WorkingSet

にて、そのスレッドが確保している物理メモリサイズ(バイト数をLong値で得られる)を得、それをグラフにした。実験では、1000 X 700 程度のJPG画像を200回読み込んでいる。横軸は回数、縦軸はメモリサイズ

○結果1

 bmpの場合。


Win2000、システムメモリ192MB(縦軸最大値も同じ)

 赤が破綻、青が回避。赤では、途中で不連続になっている。これは、要求されたメモリを確保できなくなり仮想記憶が生起したことを表す。ディスクがガンガン動き出した。青は毎回解放しているので、20数MB で一定している。


WinXP、システムメモリ1GB(縦軸最大値は512MB)

 システムメモリが1GBの場合は、この範囲では破綻はしていないが、リソースを独占しているのが分かる。逆に、メモリが十分にあるシステムでは不具合の発見が困難と言える。

○結果2

ピクチャボックスの場合。


Win2000、システムメモリ192MB(縦軸最大値も同じ)

bmp と同じ傾向となる。