調査の難しい「OutOfMemoryError」事例、5選:現場にキく、Webシステムの問題解決ノウハウ(4)
本連載は、日立製作所が提供するアプリケーションサーバ「Cosminexus」の開発担当者へのインタビューを通じて、Webシステムにおける、さまざまな問題/トラブルの解決に効くノウハウや注意点を紹介していく。現在起きている問題の解決や、今後の開発のご参考に(編集部)
今回は、Java開発者であれば、誰もが遭遇するメモリ不足エラー「OutOfMemoryError」に対する基本的な切り分け方法と、OutOfMemoryError発生につながる危険なケースを具体例を挙げて解説する。
Javaのメモリは大きく分けて2種類
Javaのメモリは大きく分けて「Javaヒープ」「Cヒープ」の2種類がある。Javaヒープは、Javaオブジェクトなどのプログラムのリソースを格納する領域、Cヒープは、JavaVM自身のリソースを格納する領域である。それぞれの領域が不足した場合、メモリ不足エラー「OutOfMemoryError」が発生する。
メモリ不足の問題の切り分け方の基本
まずは、メモリ不足がJavaヒープとCヒープのどちらで発生したかを切り分けておこう。
Javaヒープ
OutOfMemoryErrorがスローされ、JavaVMの実行が継続している場合には、Javaヒープが不足している可能性が高い。Javaヒープ不足かどうかを確定させるために、スローされたOutOfMemoryErrorのトレースを確認しよう。
java.lang.OutOfMemoryError: Java heap space <=======【*1】 at java.nio.CharBuffer.wrap(CharBuffer.java:350) <=======【*2】 at java.nio.CharBuffer.wrap(CharBuffer.java:373) at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:138) at java.lang.StringCoding.decode(StringCoding.java:173) at java.lang.String.<init>(String.java:444) at com.sun.tools.javac.zip.ZipFileIndex$ZipDirectory.readEntry(ZipFileIndex.java:805) at com.sun.tools.javac.zip.ZipFileIndex$ZipDirectory.buildIndex(ZipFileIndex.java:720) at com.sun.tools.javac.zip.ZipFileIndex$ZipDirectory.access$000(ZipFileIndex.java:652) at com.sun.tools.javac.zip.ZipFileIndex.checkIndex(ZipFileIndex.java:261) ……
OutOfMemoryErrorが「Java heap space」とともに出力され(【*1】)、OutOfMemoryErrorの発生位置がjavaソース【*2】であれば、Javaヒープ不足で確定である。
なお、Cosminexusの場合は、デフォルトでコンソールに以下のように出力されるという。
java.lang.OutOfMemoryError occurred. JavaVM aborted because of specified -XX:+HitachiOutOfMemoryAbort options.
Cヒープ
OutOfMemoryErrorがスローされず、Java VMがクラッシュしている場合には、Cヒープ不足で確定だ。この場合、コンソールには以下のように出力される。
# # An unexpected error has been detected by Java Runtime Environment: # # java.lang.OutOfMemoryError: requested 64000 bytes for GrET in <=======【*3】 # /BUILD_AREA/jdk6_04/hotspot/src/share/vm/utilities/growableArray.cpp. Out of # swap space? # # Internal Error (allocation.inline.hpp:42), pid=20594, tid=2903325616 # Error: GrET in # /BUILD_AREA/jdk6_04/hotspot/src/share/vm/utilities/growableArray.cpp # # Java VM: Java HotSpot(TM) Client VM (10.0-b19 mixed mode linux-x86) # An error report file with more information is saved as: # /tmp/hs_err_pid20594.log # # If you would like to submit a bug report, please visit: # http://java.sun.com/webapps/bugreport/crash.jsp #
OutOfMemoryErrorが「requested <n> bytes for <message>」とともに出力されるのが特徴である(【*3】)。
出力項目 | 説明 |
---|---|
n | メモリ確保要求サイズ |
message | 調査に必要な内部情報 |
そして、Native Method
まれに、Cヒープ不足でも、OutOfMemoryErrorがスローされる場合がある。この場合は、OutOfMemoryErrorのトレースが、以下のようになる。
java.lang.OutOfMemoryError at java.io.RandomAccessFile.readBytes(Native Method) <======【*4】 at java.io.RandomAccessFile.read(RandomAccessFile.java:322) at java.io.RandomAccessFile.readFully(RandomAccessFile.java:381) at com.sun.tools.javac.zip.ZipFileIndex$ZipDirectory.findCENRecord(ZipFileIndex.java:702) at com.sun.tools.javac.zip.ZipFileIndex$ZipDirectory.<init>(ZipFileIndex.java:665) at com.sun.tools.javac.zip.ZipFileIndex.checkIndex(ZipFileIndex.java:260) at com.sun.tools.javac.zip.ZipFileIndex.<init>(ZipFileIndex.java:209) at com.sun.tools.javac.zip.ZipFileIndex.getZipFileIndex(ZipFileIndex.java:115) at com.sun.tools.javac.util.DefaultFileManager.openArchive(DefaultFileManager.java:645) at com.sun.tools.javac.util.DefaultFileManager.listDirectory(DefaultFileManager.java:325) at com.sun.tools.javac.util.DefaultFileManager.list(DefaultFileManager.java:872) at com.sun.tools.javac.jvm.ClassReader.fillIn(ClassReader.java:2077) at com.sun.tools.javac.jvm.ClassReader.complete(ClassReader.java:1781
OutOfMemoryErrorのトレースの発生位置がNative Method(【*4】)となっている。これはJava APIのnative実装部分でメモリ確保に失敗しているためである。
調査の難しい「OutOfMemoryError」事例、5選
「OutOfMemoryError」発生につながる危険なケースを以下に、いくつか紹介する。今回は、特に調査の難しいCヒープ不足に絞って解説する。これらの事例を参考にして、「OutOfMemoryError」発生を防ごう。
その1:スレッド爆発!
スレッドはスタック領域としてCヒープを消費する。スレッド当たりのスタック領域サイズは、以下の通りである。
32bit OS/64bit OS | スレッド当たりのスタック領域サイズ |
---|---|
32bit OS | 512Kbytes |
64bit OS | 1Mbytes |
スレッドは、アプリケーションから明示的に作成する場合だけでなく、Java APIから暗黙的に作成する場合もある。このため、想定外にスレッドが作成されていないか、テスト段階で確認し、Cヒープ上限値を超えることがないかどうか確かめておくのがよいだろう。
スレッド数の確認は、スレッドダンプを採取したり、JDKのjconsoleなどの監視ツールを使ったりすることで可能だ。
その2:ファイル情報で使用する領域
java.io.FileクラスのdeleteOnExitメソッドはアプリケーション終了時にファイルを削除してくれる便利なメソッドだ。しかし、メソッドで削除するファイルの情報をずっと保持するため、使えば使うほどメモリを消費する。
JDK 5までは、この情報の保持先はCヒープとなっている。使いすぎに注意しないと、Cヒープを圧迫することになってしまう。
なお、JDK 6からは情報の保持先がJavaヒープになっている。このAPIを多用している場合、JDK 5までとJDK 6からとで消費するヒープが異なる点も要注意だ。JavaVMのバージョンアップなどのときに、メモリ使用サイズを確認しておくのがよいだろう。
JDKバージョン | スレッド当たりのスタック領域サイズ |
---|---|
〜5.0 | Cヒープ |
6.0〜 | Javaヒープ |
その3:大容量のI/O
ファイル入出力や通信関連のAPIは、最終的にはネイティブなシステムコールで実現されている。そのため、入出力対象データは、いったんCヒープにコピーされた後、システムコールに渡される。コピー先のバッファは、8Kbytes未満の場合は当該スレッドのスタック上に取られることになるが、それを超える場合は、Cヒープに一時的に確保される。
I/Oが完了すれば、その領域は解放される。しかし、あまりに大きなI/Oを行うと、一時的にCヒープを圧迫することになるので、気を付けよう。
バッファサイズ | バッファの確保先 |
---|---|
8Kbytes未満 | スタック |
8Kbytes以上 | Cヒープ |
その4:圧縮ライブラリで使用する領域
java.util.zipパッケージのDeflaterクラスやInflaterクラスでは、コンストラクタの処理でCヒープ領域を確保する。確保されたCヒープ領域は、オブジェクトがGCされてfinalize()が実行されるか、end()メソッドを明示的に実行するまでは解放されない。
そのため、これらのクラスのオブジェクトを大量に作成して保持していると、Cヒープを圧迫することになるので、気を付けよう。
その5:バッファオブジェクトで使用する領域
java.nio.ByteBufferクラスのallocateDirectメソッドや、java.nio.channels.FileChannelクラスのmapメソッドによりBufferオブジェクトを生成する場合、引数で指定したサイズの領域をmmapシステムコールにより確保する。
そのため、大きなサイズを指定したり、大量のBufferインスタンスを生成した場合にメモリが圧迫される可能性があります。この領域はBufferオブジェクトがガベージコレクトされるまで解放されないので、気を付けよう。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 第1回 Webアプリの問題点を「見える化」する7つ道具
- 第2回 “Stop the World”を防ぐコンカレントGCとは?
- 第3回 【実録ドキュメント】そのログ本当に必要ですか?
- 第4回 DBアクセスのトラブルは終盤で発覚しがち……
- 第5回 OutOfMemoryエラー発生!? GCがあるのに、なぜ?
- 第6回 【真夏の夜のミステリー】Tomcatを殺したのは誰だ?
- 第7回 【トラブル大捜査線】失われたコネクションを追え!
- 第8回 肥え続けるTomcatと胃を痛めるトラブルハッカー
- 第9回 JavaのGC頻度に惑わされた年末年始の苦いメモリ
- 第10回 ThreadとHashMapに潜む無限回廊は実に面白い?
- 第11回 スレッドダンプの森で覚えた死のロックへの違和感
- 第12回 アプリ開発でも、よ?く考えよう。キャッシュは大事だよ
- 第13回 DB操作の“壁”を壊すJPAが起こした「赤壁の戦い」
- 第1回 クラスタ化すると遅くなる?
- 第2回 キャッシュが性能劣化をもたらす謎を解く
- 第3回 クラスタは何台までOK?
- 第4回 マルチスレッドのいたずらに注意
- 第5回 クラスタによるアプリケーションの動的アップデート
- 第6回 APサーバからの応答がなくなった、なぜ?
- 第7回 低負荷なのにCPU使用率が100%?
- 第8回 文字化け“???”の法則とその防止策
- 第9回 メモリは足りているのに“OutOfMemory”のなぞ
- 第10回 レスポンスキャッシュでパフォーマンス向上
- 第11回 JDBC接続を高速化?PreparedCacheの活用
- 第12回 ブラウザキャッシュでパフォーマンス向上
- 第13回 ファイルアップロード/ダウンロードに潜むわな
- Eclipse上でプロファイリングを実現する
- JMeterによるWebサーバ性能評価の勘所