Javaプログラムのメモリ漏洩を回避する方法
3507 ワード
jvmにはゴミ回収メカニズムがありますが、プログラム作成が特定のルールに注意しない場合、javaプログラムのメモリが漏洩し、最終的にOutOfMemory異常が発生する可能性があります.
1.Javaメモリ漏洩の原因
JAvaのオブジェクトは使用上2種類に分けられ、参照(referenced)されるものと参照されないものに分けられます.ごみ回収は、参照されていないオブジェクトのみを回収します.参照されたオブジェクトは、使用されなくなっても回収されません.したがって,プログラムに参照される不要なオブジェクトが大量に存在する場合,メモリリークが発生する.
2.javaスタックメモリ(Heap)漏れ
jvmスタックメモリのサイズは-Xmsと-Xmxの2つのパラメータで指定します.
2.1オブジェクトが静的メンバーによって参照される
大きなオブジェクトが静的メンバーによって参照されると、メモリが漏洩します.
例:
ArrayListは、スタック上で動的に割り当てられたオブジェクトであり、通常使用が完了するとgcによって回収されるが、この例では、静的メンバlistによって参照され、静的メンバは回収されないため、この大きなArrayListがスタックメモリに留まることになる.
したがって、静的メンバーが大きなオブジェクトや集合タイプのオブジェクト(ArrayListなど)を参照しないように、静的メンバーの使用方法に特に注意する必要があります.
2.2 Stringのinternメソッド
大きな文字列でString.intern()メソッドを呼び出すと、intern()はjvmのメモリプール(PermGen)にStringを配置しますが、jvmのメモリプールはgcにされません.したがって、大きな文字列がintern()メソッドを呼び出すと、gcできないメモリが大量に発生し、メモリが漏洩します.
大きな文字列のinternメソッドを使用する必要がある場合は、-X:Max:MaxPermSizeパラメータを使用してPermGenメモリのサイズを調整する必要があります.
2.3ストリームを読み取っても閉じない
開発中にストリームを閉じるのを忘れがちなため、メモリの漏洩が発生します.各ストリームはオペレーティングシステムのレベルで開いているファイルハンドルに対応しているため、ストリームが閉じられていないため、オペレーティングシステムのファイルハンドルが常に開いている状態になり、jvmはオペレーティングシステムが開いているファイルハンドルを追跡するためにメモリを消費します.例:
この問題を解決するには、java 8の前のバージョンでfinallyにクローズ操作を追加できます.
JAva 8ではtry-with-resources文を使用できます.
ネットワーク接続やデータベース接続なども接続のクローズに注意し、接続プールを採用している場合は、クローズ操作は接続プールが担当し、プログラムでは処理しなくてもよい.
2.4 hashCode()およびequals()メソッドを実装していないオブジェクトをHashSetに追加する
これは簡単だがよくあるシーンだ.通常、Setは重複するオブジェクトをフィルタリングしますが、hashCode()とequals()が実装されていない場合、重複するオブジェクトはSetに追加され続け、削除する機会はありません.
したがってクラスにhashCode()とequals()メソッドを加えることは良いプログラミング習慣である.この機能はLombokの@EqualsAndHashCodeで簡単に実現できます.
3.メモリリークの検出方法
3.1 gcログの記録
jvmパラメータに-verbose:gcを指定することで、メモリの使用を分析するためにgcごとの詳細を記録できます.
3.2 profilingを行う
Visual VMまたはjdkに付属のJava Mission Controlでメモリ解析を行います.
3.3コード審査
コードレビューと静的コードチェックにより、メモリ漏洩の問題を引き起こすエラーコードが検出されました.
4.まとめ
コードレベルのチェックは、一部のメモリ漏洩の問題を発見するのに役立ちますが、本番環境でのメモリ漏洩は、大きな同時シーンで発生する問題が多いため、早期に発見することは容易ではありません.そのため、圧力テストツールで圧力テストを行い、潜在的なメモリ漏れの問題を事前に発見する必要があります.
1.Javaメモリ漏洩の原因
JAvaのオブジェクトは使用上2種類に分けられ、参照(referenced)されるものと参照されないものに分けられます.ごみ回収は、参照されていないオブジェクトのみを回収します.参照されたオブジェクトは、使用されなくなっても回収されません.したがって,プログラムに参照される不要なオブジェクトが大量に存在する場合,メモリリークが発生する.
2.javaスタックメモリ(Heap)漏れ
jvmスタックメモリのサイズは-Xmsと-Xmxの2つのパラメータで指定します.
2.1オブジェクトが静的メンバーによって参照される
大きなオブジェクトが静的メンバーによって参照されると、メモリが漏洩します.
例:
private Random random = new Random();
public static final ArrayList list = new ArrayList(1000000);
for (int i = 0; i < 1000000; i++) { list.add(random.nextDouble()); }
ArrayListは、スタック上で動的に割り当てられたオブジェクトであり、通常使用が完了するとgcによって回収されるが、この例では、静的メンバlistによって参照され、静的メンバは回収されないため、この大きなArrayListがスタックメモリに留まることになる.
したがって、静的メンバーが大きなオブジェクトや集合タイプのオブジェクト(ArrayListなど)を参照しないように、静的メンバーの使用方法に特に注意する必要があります.
2.2 Stringのinternメソッド
大きな文字列でString.intern()メソッドを呼び出すと、intern()はjvmのメモリプール(PermGen)にStringを配置しますが、jvmのメモリプールはgcにされません.したがって、大きな文字列がintern()メソッドを呼び出すと、gcできないメモリが大量に発生し、メモリが漏洩します.
大きな文字列のinternメソッドを使用する必要がある場合は、-X:Max:MaxPermSizeパラメータを使用してPermGenメモリのサイズを調整する必要があります.
2.3ストリームを読み取っても閉じない
開発中にストリームを閉じるのを忘れがちなため、メモリの漏洩が発生します.各ストリームはオペレーティングシステムのレベルで開いているファイルハンドルに対応しているため、ストリームが閉じられていないため、オペレーティングシステムのファイルハンドルが常に開いている状態になり、jvmはオペレーティングシステムが開いているファイルハンドルを追跡するためにメモリを消費します.例:
BufferedReader br = new BufferedReader(new FileReader(path));
return br.readLine();
この問題を解決するには、java 8の前のバージョンでfinallyにクローズ操作を追加できます.
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
JAva 8ではtry-with-resources文を使用できます.
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
ネットワーク接続やデータベース接続なども接続のクローズに注意し、接続プールを採用している場合は、クローズ操作は接続プールが担当し、プログラムでは処理しなくてもよい.
2.4 hashCode()およびequals()メソッドを実装していないオブジェクトをHashSetに追加する
これは簡単だがよくあるシーンだ.通常、Setは重複するオブジェクトをフィルタリングしますが、hashCode()とequals()が実装されていない場合、重複するオブジェクトはSetに追加され続け、削除する機会はありません.
したがってクラスにhashCode()とequals()メソッドを加えることは良いプログラミング習慣である.この機能はLombokの@EqualsAndHashCodeで簡単に実現できます.
3.メモリリークの検出方法
3.1 gcログの記録
jvmパラメータに-verbose:gcを指定することで、メモリの使用を分析するためにgcごとの詳細を記録できます.
3.2 profilingを行う
Visual VMまたはjdkに付属のJava Mission Controlでメモリ解析を行います.
3.3コード審査
コードレビューと静的コードチェックにより、メモリ漏洩の問題を引き起こすエラーコードが検出されました.
4.まとめ
コードレベルのチェックは、一部のメモリ漏洩の問題を発見するのに役立ちますが、本番環境でのメモリ漏洩は、大きな同時シーンで発生する問題が多いため、早期に発見することは容易ではありません.そのため、圧力テストツールで圧力テストを行い、潜在的なメモリ漏れの問題を事前に発見する必要があります.