2011-02-09 16 views
26

可能性の重複:
Creating a memory leak with JavaJavaでメモリリークを引き起こす最も簡単な方法は?

Javaのメモリリークが発生する最も簡単な方法は何ですか?

+0

あなたは、人為的な例または非常に一般的なプログラミングミスをお探しですか? – mikerobi

+0

人為的な例が最適です。 –

+0

メモリリークは、使用される予定のないオブジェクトが参照を持っている場合に作成されます。ほとんどどんなプログラムでも書くことができるのは、メモリリークの人為的な例です。 – OrangeDog

答えて

27

はあなたがJavaで本当に「リークメモリ」ことができない場合を除き:

  • インターン文字列
  • は、JNIによって呼び出されたネイティブコード内のクラス
  • リークメモリを生成
  • 物事への参照を保持しているあなたいくつかの忘れられた、または不明瞭な場所には望んではいけません。

最後のケースに興味があります。一般的なシナリオは以下のとおりです。特に内部クラス

  • キャッシュで行わ

    • リスナー、。

    良い例は、に次のようになります。

    • モーダルウィンドウの潜在的に無制限に起動するのSwing GUIを構築します。
    • モーダルウィンドウを持って、その初期化中にこのような何かを:
       
      StaticGuiHelper.getMainApplicationFrame().getOneOfTheButtons().addActionListener(new ActionListener(){ 
          public void actionPerformed(ActionEvent e){ 
          // do nothing... 
          } 
      }) 
      

    登録されているアクションは何もしませんが、それは、モーダルウィンドウがさえ漏れの原因となる、閉じた後、永遠に記憶に残るようになります - リスナーは決して登録解除されず、各匿名の内部クラスオブジェクトは外部オブジェクトへの参照(不可視)を保持するためです。さらに、モーダルウィンドウから参照されるオブジェクトには、漏れの可能性もあります。

    これは、EventBusなどのライブラリがデフォルトで弱い参照を使用する理由です。

    リスナー以外の典型的な例はキャッシュですが、良い例は考えられません。

  • +4

    interned文字列は実際にメモリリークではなく、ガベージコレクションされる可能性もあります。問題は、(通常の実装では)メモリの残りの部分よりも小さい特殊なメモリ領域(PermGen)にあり、より簡単にいっぱいになることだけです。 –

    +0

    あなたは正しいです。 interned文字列は "本当の"リークではありません(また、 "実際のリーク"はjvmでは不可能です)。それにもかかわらず、パーマは、他のすべてが失敗し、その内容が主要なコレクションに残っている場合にのみ収集されるため、実際のメモリ問題のいくつかの原因の1つです。また、プログラムから参照されていないにもかかわらず、中身の文字列はスペースを取る。その意味で、彼らはあなたが得ることができるようにリークに近いです。 – fdreger

    +0

    最高の回答の1つ:https://www.reddit.com/r/AMA/comments/7z8tw0/people_who_are_experts_in_their_fieldhobby_ama/dumd8y8/ –

    3
    1. は定期的に収集

    を保持しているクラスのインスタンスへの参照を削除しないでください収集

  • に新しいオブジェクトを追加したクラスのスコープでのオブジェクトのコレクションを作成しますので、そこに常にコレクションの参照であり、コレクションを所有するオブジェクトのインスタンスは、ガベージコレクタがそのメモリを決してクリーンアップしないため、時間の経過とともに「リーク」が発生します。

  • 8
    public static List<byte[]> list = new ArrayList<byte[]>(); 
    

    そして、(大)配列を削除せずに追加します。ある時点では、疑わしいことなくメモリが使い果たされます。

    Javaでは、オブジェクトを逆参照すると(範囲外になる)、ガベージコレクションされます。だからあなたはメモリの問題を抱えるために、その参照を保持する必要があります。

    +0

    これでメモリが足りなくなりますが、オブジェクト参照を破るために何もしないと、どうやってリークすることができますか? – mikerobi

    +5

    @mikerobi - メモリリークは、メモリを掃除せずに(そしてメモリを使わずに)メモリを「占有」するときです。ただし、オブジェクトを参照解除すると、ガベージコレクションされます。 – Bozho

    +1

    私はそれを理解していますが、私はこれをすべてのケースで漏れとはみなしません。誤ってクラス変数を静的にすると、間違いなくリークが発生します。長時間実行されているプロセスでグローバル変数として使用していると、リークが発生する可能性があります。プログラムが終了するまでデータが保持されることが意図されている場合は、リークではありません。無限ループがあなたの記憶を消耗させるという事実は、これがリークであるという事実とは関係ありません。多くのリークは、新しいデータを継続的に割り当てない限り気付かれませんが、孤立したメモリが固定されている場合は、まだ漏れています。 – mikerobi

    8

    は、ここで私が最も投票の答えに何を読んでから、簡単な例

    public class finalizer { 
        @Override 
         protected void finalize() throws Throwable { 
         while (true) { 
          Thread.yield(); 
         } 
        } 
    
        public static void main(String[] args) { 
         while (true) { 
          for (int i = 0; i < 100000; i++) { 
           finalizer f = new finalizer(); 
          } 
    
          System.out.println("" + Runtime.getRuntime().freeMemory() + " bytes free!"); 
         } 
        } 
    } 
    
    +0

    この例でどのようにメモリリークを達成しているのか少し説明できますか? – TheBlueNotebook

    +0

    確かにわかりませんが、このコードは動作しています。少なくとも、私のPCを殺してしまい、Eclipseの終了後もバックグラウンドでプロセスが保持されています。 – pescamillam

    +1

    @TheBlueNotebook彼が打ち破ったfinalizeメソッドは、Javaが通常メモリを解放しようとしているときにオブジェクト。彼の主な方法では、100Kのファイナライザを作成し、JVMにすべてのメモリを解放するように指示します。 JVMはこれを丁寧に行い、実際にメモリを解放する前にファイナライズを呼び出します。オブジェクトが削除されることはありませんが、メインループは続行され、決して削除されない別の100Kオブジェクトが作成され、次に別のオブジェクトが作成され、別のオブジェクトが作成されます。 – Jeutnarg

    3

    だ、あなたはおそらくC-のようなメモリリークを求めています。さて、garbaggeコレクションがあるので、オブジェクトを割り振ることはできません。すべての参照を緩和して、まだメモリを占有してください。それは重大なJVMバグです。

    一方、オブジェクトへの参照でスレッドが実行され、スレッドの参照が緩くなる可能性があるため、スレッドがリークする可能性があります。あなたはまだAPIを介してスレッドの参照を取得することができます -

    0

    http://www.exampledepot.com/egs/java.lang/ListThreads.htmlは、この単純なクラス試してみてください(この文脈で、または漏れ)

    public class Memory { 
    private Map<String, List<Object>> dontGarbageMe = new HashMap<String, List<Object>>(); 
    
    public Memory() { 
        dontGarbageMe.put("map", new ArrayList<Object>()); 
    } 
    
    public void useMemInMB(long size) { 
        System.out.println("Before=" + getFreeMemInMB() + " MB"); 
    
        long before = getFreeMemInMB(); 
        while ((before - getFreeMemInMB()) < size) { 
         dontGarbageMe.get("map").add("aaaaaaaaaaaaaaaaaaaaaa"); 
        } 
    
        dontGarbageMe.put("map", null); 
    
        System.out.println("After=" + getFreeMemInMB() + " MB"); 
    } 
    
    private long getFreeMemInMB() { 
        return Runtime.getRuntime().freeMemory()/(1024 * 1024); 
    } 
    
    public static void main(String[] args) { 
        Memory m = new Memory(); 
        m.useMemInMB(15); // put here apropriate huge value 
    } 
    } 
    
    +0

    ここでは最も複雑な単純な例です。 ;) –

    +0

    リークはどこですか? GCの後で解放されたリストではありませんか? –

    15

    「メモリリーク、コンピュータサイエンスのを、ときにコンピュータプログラムを発生しますメモリは消費されますが、オペレーティングシステムに戻すことはできません。 (ウィキペディア)

    簡単な答えは次のとおりです。 Javaは自動メモリー管理を行い、必要のないリソースを解放します。これを止めることはできません。それは常にリソースを解放することができるでしょう。手動メモリ管理のプログラムでは、これは異なります。 malloc()を使用してCでメモリを取得することはできません。メモリを解放するには、mallocが返したポインタが必要で、free()を呼び出します。しかし、ポインタをもう持たない(上書きする、または寿命を超えた)場合は、残念ながらこのメモリを解放することができず、したがってメモリリークが発生します。

    これまでのその他の回答は、私の定義では実際にはメモリリークではありません。彼らはすべて、無意味なもので本当に素早くメモリを満たすことを目指しています。しかし、いつでもあなたが作成したオブジェクトを逆参照し、メモリを解放することができます - >漏れはありません。彼の解決策が効果的にガベージコレクタを無限ループで強制的に "クラッシュ"させていることを認めなければならないので、acconradの答えはかなり近いです。

    長い答えは次のとおりです。JNIを使​​用してJavaのライブラリを作成するとメモリリークが発生する可能性があります。手動でメモリを管理してメモリリークが発生する可能性があります。このライブラリを呼び出すと、Javaプロセスがメモリをリークします。または、JVMにバグがあると、JVMがメモリを失います。おそらくJVMにはバグがあります。ガベージコレクションはそれほど簡単ではないので、既知のものもあるかもしれませんが、それでもバグです。設計上これは不可能です。あなたは、そのようなバグによって影響を受けるいくつかのJavaコードを求めているかもしれません。申し訳ありませんが、私はそれを知らないし、とにかく次のJavaバージョンではもはやバグではないかもしれません。

    +1

    「いつでも、あなたが作成したオブジェクトを逆参照し、メモリを解放することができます」。同意しません。クラスの実装者は、外部からオブジェクトハンドルを隠すことができます。 –

    +0

    @trinithis:膨大な量のメモリを割り当ててメモリを個人的に浪費するオブジェクトがある場合、そのオブジェクトも破棄せずにオブジェクトを強制的に解放することはできません。しかしこの場合、それはまだメモリを浪費していて、漏れではありません。メモリを解放することができます。無駄なメモリを参照するオブジェクトがもう参照されなくなると解放されます。それとも私はあなたを誤解しましたか? – yankee

    +0

    私はあなたが「間接参照」の意味を誤解したと思います。私は単語のCの意味を考えていた。 –

    0

    答えのほとんどはCスタイルのメモリリークではないようです。

    私は、あなたにメモリ不足の例外を与えるバグを含むライブラリクラスの例を追加したいと考えました。繰り返しますが、これは真のメモリリークではありませんが、予想外のメモリ不足の例です。

    public class Scratch { 
        public static void main(String[] args) throws Exception { 
         long lastOut = System.currentTimeMillis(); 
         File file = new File("deleteme.txt"); 
    
         ObjectOutputStream out; 
         try { 
          out = new ObjectOutputStream(
            new FileOutputStream("deleteme.txt")); 
    
          while (true) { 
           out.writeUnshared(new LittleObject()); 
           if ((System.currentTimeMillis() - lastOut) > 2000) { 
            lastOut = System.currentTimeMillis(); 
            System.out.println("Size " + file.length()); 
            // out.reset(); 
           } 
          } 
         } catch (Exception e) { 
          e.printStackTrace(); 
         } 
        } 
    } 
    
    class LittleObject implements Serializable { 
        int x = 0; 
    } 
    

    あなたは使用している場合、メモリリークが発生します

    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4363937

    0

    次極めて不自然Boxクラスで元のコードやバグの説明を見つけるでしょう。このクラスへのputであるオブジェクトは、最終的に(正確にはputへの別の呼び出しの後に...同じオブジェクトがそれに再putを持たない限り)外界にアクセスできません。これらは、このクラスを通して参照解除することはできませんが、このクラスは確実に収集できません。これは本当のリークです。私はこれが実際には工夫されていることを知っていますが、偶然にも同様のケースが可能です。

    import java.util.ArrayList; 
    import java.util.Collection; 
    import java.util.Stack; 
    
    public class Box <E> { 
        private final Collection<Box<?>> createdBoxes = new ArrayList<Box<?>>(); 
        private final Stack<E> stack = new Stack<E>(); 
    
        public Box() { 
         createdBoxes.add(this); 
        } 
    
        public void put (E e) { 
         stack.push(e); 
        } 
    
        public E get() { 
         if (stack.isEmpty()) { 
          return null; 
         } 
         return stack.peek(); 
        } 
    } 
    
    関連する問題