Javaのメモリリークの典型的なケース

ほとんどの開発者は、Javaのガベージコレクターが、プログラマーがメモリの使用規則とその動作時期を完全に忘れることを可能にする普遍的なメカニズムではないことを知っています。 ユビキタスなJavaアプリケーションでのメモリリークの一般的なケースを以下に説明します。

だから、すべてのJavaプログラマーが覚えておくべきこと。



典型的なメモリリークの状況:



画像

ガベージコレクターは未使用のオブジェクトを定期的に収集しますが、ヒープ使用スケジュールが自信を持って正しく作成されていることがわかります。



この状況の原因は何ですか?



文字列操作


最も一般的な状況は、Javaアプリケーションでメモリリークが発生する場合です。

理解のために。 文字列を操作するときに問題があるため、Javaで文字列のsubstring()メソッドを呼び出すなどの操作を実行すると、文字列インスタンスは長さとオフセット変数の変更された値(charシーケンスの長さとオフセット)でのみ返されることに注意してください さらに、5000文字の長さの文字列を取得し、substring()メソッドを使用して接頭辞のみを取得する場合、5000文字は引き続きメモリに格納されます。

多くのメッセージを受信して​​処理するシステムの場合、これは重大な問題になる可能性があります。

この問題を回避するには、次の2つのオプションを使用できます。



String prefix = new String(longString.substring(0,5)); //

String prefix = longString.substring(0,5).intern(); //








Zorkusインターン文字列の2番目のオプションに関する重要な注意:インターンされた文字列は、ヒープスペースではなく、permgenスペースに保存されます。 ガベージコレクションは、heap-e / young / tenuredメモリプールではなく、個別のルールに従って行われます。



同様に、split()メソッドを使用すると同様の問題が発生することを忘れないでください。



ObjectInputStreamおよびObjectOutputStream


ObjectInputStreamクラスとObjectOutputStreamクラスは、コピーの代わりにそれらを渡すために使用したすべてのオブジェクトへの参照を保存します。 これにより、連続使用中(たとえば、ネットワーク通信中)にメモリリークが発生します。

この問題を解決するには、定期的にreset()メソッドを呼び出す必要があります。



スレッドとそのスタック


JavaのThreadクラスの各インスタンスは、そのスタックにメモリを割り当てます(デフォルトでは、これは512 KBです。-Xssパラメーターを使用して変更されます)。 複数のスレッドを使用する最適化されていないアプリケーションは、不当に高いメモリ消費につながる可能性があります。



非静的内部クラス


使用する各非静的内部クラスには、外部クラスへの参照が格納されます。 これにより、オブジェクトの大きなグラフが保存され、メモリの使用に悪影響を及ぼします。 明らかに外部クラスへの参照を使用する必要がない状況では、内部クラスを静的に実装します。



オブザーバーパターンと関連する脅威


多くの場合、Observerパターンを使用するとメモリリークが発生します。 ご存知のように、Observerは特定のアクションの通知をサブスクライブしているリスナーのリストを保持しています。 さらに、このオブザーバのサブスクライバである特定のクラスを使用しなくなった場合、GCへのリンクはObserverインスタンス自体に格納されるため、GCはそれを「収集」できません。



シングルトン


シングルトンインスタンスが初期化されると、アプリケーションのライフタイム全体にわたってメモリ内に残ります。 結果として、このインスタンスはコレクターによってアセンブルできません。 このパターンは、メモリ内の永続的なストレージの実際の要件によって正当化される場合にのみ使用してください。



ThreadLocal変数


ThreadLocal変数への参照は、それに関連付けられたスレッドによって使用されます。 ほとんどのアプリケーションサーバーでは、スレッドはプールで再利用されるため、ThreadLocalデータはGCによって収集されません。 アプリケーション自体がこれらの値をクリアすることを気にしない場合、深刻なメモリリークが発生します。



可変静的オブジェクト


また、静的の誤用は、Javaアプリケーションでのメモリリークの一般的な発生です。 静的変数は、そのクラスによって格納され、その結果、ローダー(クラスローダー)によって格納されます。 外部使用のため、ガベージコレクターがこのインスタンスを収集しない可能性が高くなります。 また、多くの場合、静的変数には情報がキャッシュされるか、複数のスレッドで使用される状態が保存されます。 別の例は、静的コレクションです。 建築設計の良いトーンは、可変の静的オブジェクトを完全に回避することです。多くの場合、より良い代替手段があります。



オブジェクトを作成する


2つのケースを検討します。

事例1:



Elem e;

e = new HeavyElem();

e = new HeavyElem();








ケース2:



Elem e;

e = new HeavyElem();

e = null;

e = new HeavyElem();








ほとんどの場合、これはコレクターに最初に作成されたHeavyElemの未使用のインスタンスを収集するように明確に伝えるため、2番目のケースの方がより正確です。



クラスローダー


クラス参照はローダーによって使用され、通常はクラスローダー自体がビルドされるまでGCによってコンパイルされません。 これは、たとえば、OSGiコンテナーからアプリケーションをアンロードする状況でしばしば発生します。 また、これを念頭に置いて適切な対策を講じる必要があります。



それらを避ける方法は?



そして今、メモリリークの問題を回避する方法に関するいくつかのヒントを修正します:

1.プロファイラーを使用します。 プロファイラーは、ヒープ上にあるオブジェクトを確認するのに役立ちます(また、インスタンスごとに直接表示することもできます)。これにより、初期段階でリークをキャッチできます。

2.特にプログラムが大量のテキストデータの処理に取り組んでいる場合は、文字列操作を注意して使用してください。

3.非静的内部クラス、静的変数が必要な場合は常に注意してください

4.データが処理された後、オブジェクトのコレクションをクリーンアップし、それ以上使用する必要はありません。



適切なコードを作成し、メモリ処理のルールを忘れないでください!



All Articles