web-dev-qa-db-ja.com

Javaでメモリリークが発生する可能性がありますか?

この質問は何度も聞かれます。答える良い方法は何ですか

34
javaguy

Javaでメモリリークが発生する可能性はありますか?

答えは、それはあなたが話しているメモリリークの種類に依存するということです。

従来のC/C++メモリリークは、アプリケーションがオブジェクトの処理を完了したときにオブジェクトをfreeまたはdisposeに無視すると発生し、リークします。循環参照はこのサブケースであり、アプリケーションはいつfree/disposeを実行するかを知ることが困難であり、結果としてそれを怠ります。関連する問題は、アプリケーションがオブジェクトを解放した後にオブジェクトを使用する場合、またはオブジェクトを2回解放しようとする場合です。 (後者の問題は、メモリリーク、または単にバグと呼ぶことができます。いずれにしても...)

Javaおよびその他(完全に1)管理対象言語ほとんどは、GCが到達不能になったオブジェクトの解放を処理するため、これらの問題に悩まされることはありません。 (確かに、ダングリングポインターやダブルフリーの問題は存在せず、C/C++の「スマートポインター」やその他の参照カウントスキームの場合のように、サイクルは問題になりません。)

ただし、場合によっては、JavaのGCは、(プログラマーの観点から)ガベージコレクションする必要のあるオブジェクトを見逃します。これは、GCがオブジェクトに到達できないことを認識できない場合に発生します。

  • プログラムのロジック/状態は、ある変数を使用する実行パスが発生しないようなものである可能性があります。開発者はこれを明白であると見なすことができますが、GCは確信が持てず、注意を怠ります(必要に応じて)。
  • プログラマーはそれについて間違っている可能性があり、GCは、そうでなければ参照がぶら下がる可能性のあるものを回避しています。

(Javaのメモリリークの原因は単純な場合もあれば、非常に微妙な場合もあることに注意してください。微妙なものについては、@ jonathan.coneの回答を参照してください。最後の原因は潜在的に外部リソースに関係していますとにかく処理するためにGCに依存すべきではありません。)

いずれにせよ、不要なオブジェクトをガベージコレクションできない状況が発生し、メモリを拘束することになります...メモリリーク。

次に、a Javaアプリケーションまたはライブラリが、手動で管理する必要のあるネイティブコードを介してオフヒープオブジェクトを割り当てることができるという問題があります。アプリケーション/ライブラリにバグがあるか、誤って使用されている場合は、次のことができます。ネイティブメモリリークを取得します(例: Androidビットマップメモリ​​リーク ...この問題はAndroidの新しいバージョンで修正されていることに注意してください。)


1-私はいくつかのことをほのめかしています。一部のマネージ言語では、従来のストレージリークを作成できるアンマネージコードを記述できます。他のいくつかの管理言語(またはより正確には言語実装)は、適切なガベージコレクションではなく参照カウントを使用します。参照カウントベースのストレージマネージャーは、サイクルを中断するために何か(つまり、アプリケーション)を必要とします...そうしないと、ストレージリークが発生します。

21
Stephen C

はい。 GCを使用している場合でも、メモリリークが発生する可能性があります。たとえば、データベースの結果セットなど、手動で閉じる必要のあるリソースを保持している場合があります。

10
Markus Johnsson

Javaはガベージコレクターを使用して未使用のオブジェクトを収集することを考えると、ダングリングポインターを使用することはできません。ただし、オブジェクトを必要以上にスコープ内に保持することはできます。メモリリークと見なされる可能性があります。詳細については、こちらをご覧ください: http://web.archive.org/web/20120722095536/http://www.ibm.com:80/developerworks/rational/library/05/ 0816_GuptaPalanki /

これか何かのテストを受けていますか?それは少なくともそこにA +があるからです。

10
AnthonyJoeseph

答えは響き渡るはいですが、これは通常、プログラミングモデルの結果であり、 JVMに何らかの欠陥があることを示しています。これは、フレームワークのライフサイクルが実行中のJVMとは異なる場合に一般的です。いくつかの例は次のとおりです。

  1. コンテキストの再読み込み
  2. オブザーバー(リスナー)の逆参照に失敗しました
  3. リソースの使用を終了した後、リソースをクリーンアップするのを忘れています *

*-最後の1つを解決するために数十億ドルのコンサルティングが行われました

5
jonathan.cone

はい、あなたのJavaアプリケーションは、ガベージコレクターが解放できない時間の経過とともにメモリを蓄積する可能性があるという意味で。

不要/不要なオブジェクトへの参照を維持することにより、それらがスコープから外れることはなく、それらのメモリが要求されることもありません。

4
Tyler

はい、オブジェクトを逆参照しないと、オブジェクトがガベージコレクションされることはなく、メモリ使用量が増加します。ただし、Javaの設計方法のため、これを実現するのは困難ですが、他の一部の言語では、これを実現しないのが難しい場合があります。

編集:Amokraneのリンクを読んでください。それは良いです。

3
pstanton

はい、可能です。

効果的なJavaには、配列を使用して実装されたスタックを含む例があります。ポップ操作でインデックス値を単純にデクリメントすると、メモリリークが発生する可能性があります。なぜですか?配列にはまだ参照があるためです。ポップされた値への参照があり、スタックオブジェクトへの参照が残っているため、このスタック実装で行う正しいことは、ポップされた配列インデックスで明示的なnull割り当てを使用して、ポップされた値への参照をクリアすることです。

3
dteoh

Effective Java は、「メモリリーク」の2つの理由を示しています。

  • オブジェクト参照をCacheに入れたら、そこにあることを忘れてください。参照は、無関係になるずっと前にキャッシュに残ります。解決策は、キャッシュを WeakHashMap として表すことです。
  • クライアントがコールバックを登録し、それらを明示的に再登録しないAPIで。解決策は、それらへの弱い参照のみを格納することです。
2
Trivikram

簡単な答え:

有能なJVMにはメモリリークはありませんが、未使用のオブジェクトがすべてガベージコレクションされているわけではないため、必要以上のメモリを使用できます。また、Javaアプリ自体が、不要になったオブジェクトへの参照を保持する可能性があり、これによりメモリリークが発生する可能性があります。

2

はい、それは、プログラムがオブジェクトへの参照を誤って保持し、二度と使用されないため、GCによってクリーンアップされない場合に発生する可能性があります。

その一例は、開いているストリームを閉じるのを忘れることです。

class MemoryLeak {

    private void startLeaking() throws IOException {
        StringBuilder input = new StringBuilder();
        URLConnection conn = new URL("www.example.com/file.txt").openConnection();

        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));        

        while (br.readLine() != null) {
            input.append(br.readLine());
        }
    }

    public static void main(String[] args) throws IOException {
        MemoryLeak ml = new MemoryLeak();
        ml.startLeaking();
    }
}
1
Johnny

簡単な答えの1つは、JNIを使​​用していない限り、JVMがPOJOの[プレーンオールドJavaオブジェクト]の初期化をすべて処理します。JNIを使​​用してメモリを割り当てた場合は、そのメモリを自分で処理する必要があるネイティブコード。

0
Swaranga Sarma

はい。メモリリークは、アプリによってメモリマネージャに解放されない未使用のメモリです。

Javaコードはデータ構造にアイテムを格納しますが、アイテムがそこから削除されることはなく、OutOfMemoryErrorまでメモリがいっぱいになります。

void f() {
    List<Integer> w = new ArrayList<Integer>();
    while (true) {
         w.add(new Integer(42));
    }
}

この例はあまりにも明白ですが、Javaメモリエラーはより微妙になる傾向があります。たとえば、SESSIONスコープを持つ コンポーネントに巨大なオブジェクトを格納する依存性注入を使用する 、オブジェクトが使用されなくなったときに解放せずに。

64ビットの場合VMこれは、システムがあまりにも多くのIO操作をクロールするまでスワップメモリ​​スペースがいっぱいになり始めるため、悪化する傾向があります。

0
vz0