じゃじゃ馬ならし

 

OutOfMemoryError のハンドリング

はじめに

Tiger ではじまった、ソフトウェアの管理の強化ですが、Java SE 6 ではさらに強力になっています。

強化点はいろいろあるのですが、まず第一にあげたいのが、OutOfMemroyError のハンドリングです。

 

第一題 OutOfMemoryError

まずは、OutOfMemoryError です。

皆さんも痛い想い出がいろいろとあるのではないでしょうか。

Java SE 6 では OutOfMemoryError が出なくなる... ことはありません ^^;;

Tiger までは OutOfMemoryError がどこで発生しているかいまいち分かりづらかったのではないでしょうか。

それが、Java SE 6 ではちゃんと OutOfMemoryError が発生するポイントを明示するようになりました。これはうれしい!!

さっそく、サンプルで確かめてみましょう。

サンプルのソースコード OutOfMemorySample.java

このサンプルは指定したディレクトリにあるイメージファイルを 500 ミリ秒ごとに読みこんで、タイル状に表示するというサンプルです。実行するとこんな感じに表示されます。

OutOfMemorySample

jconsole を使ってヒープがどのように変化しているのか示したのが、次の図です。ここでは Old 領域である Tenured を表示しています。一直線にヒープが増加しているのがお分かりですよね。

jconsole

このサンプルはスレッドを利用してイメージの読み込みを行なうため、例外が発生してもキャッチができません。そこで、次のようにデフォルトの UncaughtExceptionHandler オブジェクトを設定してあります。

    Thread.setDefaultUncaughtExceptionHandler(
        new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable e) {
                e.printStackTrace();
                System.exit(1);
            }                
        });

さて、これを J2SE 5.0 でコンパイルして、実行してみましょう。

C:\temp>java OutOfMemorySample photo
java.lang.OutOfMemoryError: Java heap space
 
C:\temp>

これだとどこで OutOfMemoryError が発生したのか全然わかりません。このサンプルは明示的にヒープ使用量を増やしているので、どこで OutOfMemoryError が発生しているのかすぐに分かりますが、一般のアプリケーションでは分かりません。

これを Java SE 6 でコンパイル、実行すると次のようになります。

C:\temp>java OutOfMemorySample photo
java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.(DataBufferInt.java:41)
        at java.awt.image.Raster.createPackedRaster(Raster.java:458)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(Direct
ColorModel.java:1015)
        at sun.awt.image.ImageRepresentation.createBufferedImage(ImageRepresenta
tion.java:235)
        at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:
487)
        at sun.awt.image.ImageDecoder.setPixels(ImageDecoder.java:120)
        at sun.awt.image.JPEGImageDecoder.sendPixels(JPEGImageDecoder.java:97)
        at sun.awt.image.JPEGImageDecoder.readImage(Native Method)
        at sun.awt.image.JPEGImageDecoder.produceImage(JPEGImageDecoder.java:119
)
        at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.j
ava:246)
        at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172)
        at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)
java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.(DataBufferInt.java:41)
        at java.awt.image.Raster.createPackedRaster(Raster.java:458)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(Direct
ColorModel.java:1015)
        at sun.awt.image.ImageRepresentation.createBufferedImage(ImageRepresenta
tion.java:235)
        at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:
487)
        at sun.awt.image.ImageDecoder.setPixels(ImageDecoder.java:120)
        at sun.awt.image.JPEGImageDecoder.sendPixels(JPEGImageDecoder.java:97)
        at sun.awt.image.JPEGImageDecoder.readImage(Native Method)
        at sun.awt.image.JPEGImageDecoder.produceImage(JPEGImageDecoder.java:119
)
        at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.j
ava:246)
        at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172)
        at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)

ちゃんとスタックとレースが表示されました。それも適当な箇所ではなくて、ちゃんとヒープ領域をアロケーションしている部分になっています。

ちなみにこれは JDK で実行しているため、コアライブラリでも行数表示が行なわれています。

JRE で実行するとコアライブラリの行数は表示されません。これは、JRE ではコアライブラリが -g:none というオプションでコンパイルされているからです。

それにしても、OutOfMemoryError でスタックトレースが取得できるというのは、とても嬉しいことです。でも、なんで今までできなかったんでしょうね。

 

(Nov. 2005)

(改訂 Feb. 2007) jconsole、ツール類の解説を独立させ、OutOfMemoryErrror のみに変更