質問
⚠️ この事例は公開されてから1年以上経過しています。 情報が古い可能性がありますので、ご注意ください。
🕓 事例公開日 : 2025-03-14
【背景】
システム再起動時にヒープダンプが出力されているが、特定日のファイルが見つからない状況が発生した。また、過去のヒープダンプを解析したところ、特定のクラスが大量のメモリを使用していることが判明し、その原因を調査したい。Q1.
yyyy年MM月dd日に出力されたヒープダンプはどこに保存されているか教えてください。Q2.
NativeRegExp.executeRegExpクラスが1.3GBものメモリを使用している原因は何でしょうか。どのような処理で実行されるクラスなのか知りたいです。Q3.
十数MBのファイルをアップロードしてBASE64エンコード処理を行うと、java.util.Arrays.copyOfが1GB超のメモリを使用してしまいます。この処理でメモリ使用量が大幅に増加する理由を教えてください。回答
A1.
yyyy年MM月dd日の再起動により出力されたヒープダンプファイルは、以下の場所に出力されています。/opt/accel-mart/resin/log/heap.hprof
なお、ヒープダンプの出力場所は再起動の原因によって異なります:
・JVM側でOutOfMemoryErrorが検知された場合:
/opt/accel-mart/resin/(ZIP圧縮される)
・Resinのヘルスチェックにより異常が検知された場合:
/opt/accel-mart/resin/log/(ZIP圧縮されない)
A2.
NativeRegExp.executeRegExpクラスの大量メモリ使用は、ImJsonオブジェクトのparseJSONメソッドが原因と考えられます。parseJSON実行時は、変換処理だけでなく正規表現の妥当性チェックも行われており、その際のデータがスタックされるため、元データの情報が多い場合にチェックデータが蓄積される可能性があります。そのため、十数MBの情報であっても1GB付近のメモリを使用してしまう場合があります。
ヒープダンプから確認したところ、「forma/normal/view/list_view/foo_bar」に関連したリクエストが原因と推測されます。foo_barのFormaアプリで大量データを受け渡すような処理を実行していないかご確認ください。
なお、チェックデータは処理中のみメモリ上に蓄積され、処理後はGCの対象となります。ただし、変換処理に時間がかかる場合はオブジェクトがOLD領域に入るため、FULL GCが実行されないと解放されない可能性があります。A3.
ロジックフロー定義のマッピング内でBASE64エンコードを行う際、以下の動作によりメモリ使用量が増加します:1. ファイルデータがByteArrayOutputStreamによってメモリ上に展開される
2. BASE64.encodeの引数として渡すためにByteArrayOutputStream.toByteArray()でファイルデータがコピーされ、さらにメモリ上に展開される
3. 展開されたメモリ上のデータはJavaのGCのタイミングで解放される
この動作によりファイルサイズより数倍のメモリを使用する可能性はありますが、十数MBのファイルで1GB以上使用する状態は通常考えにくい状況です。
処理に渡されている十数MBのファイル自体に何らかの要因がある可能性があります。事象が再現するファイルがございましたら、詳細な調査のためにご提供をお願いいたします。