2

Java でメモリ リークを示すサンプル プログラムを作成します。

public class MemoryLeakTest {
     static int depth = 0;
     int number=0;
     MemoryLeakTest mobj;

     MemoryLeakTest(){
      number = depth;
      if(depth < 6500){
          depth++;
          mobj = new MemoryLeakTest();
      }
     }

     protected void finalize(){
      System.out.println(number + " released.");
     }

    public static void main(String[] args) {
        try{
             System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
             System.out.println("Free Memory in starting "+ Runtime.getRuntime().freeMemory());
             MemoryLeakTest testObj = new MemoryLeakTest();
             System.out.println("Free Memory in end "+ Runtime.getRuntime().freeMemory());
             System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
        }
        catch(Exception exp){}
        finally{
            System.out.println("Free Memory"+ Runtime.getRuntime().freeMemory());
            System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
        }
    }

}

N in の値を変更して実行しif(depth < N)ます。ここに結果があります。

深さが1000の場合

init = 16777216(16384K) used = 288808(282K) commit = 16252928(15872K) max = 259522560(253440K) 15872K) 最大 = 259522560(253440K) 空きメモリ 15964120 初期化 = 16777216(16384K) 使用済み = 288808(282K) コミット済み = 16252928(15872K) 最大 = 259522560(253440K)

深度が 1500 の場合

init = 16777216(16384K) used = 288808(282K) commit = 16252928(15872K) max = 259522560(253440K) 15872K) 最大 = 259522560(253440K) 空きメモリ 15873528 初期化 = 16777216(16384K) 使用済み = 379400(370K) コミット済み = 16252928(15872K) 最大 = 259522560(253440K)

depth が 6000 の場合

init = 16777216(16384K) used = 288808(282K) commit = 16252928(15872K) max = 259522560(253440K) 15872K) 最大 = 259522560(253440K) 空きメモリ 15692784 初期化 = 16777216(16384K) 使用済み = 560144(547K) コミット済み = 16252928(15872K) 最大 = 259522560(253440K)

depth が 6500 の場合 (スレッド「main」で例外 java.lang.StackOverflowError)

init = 16777216(16384K) used = 288808(282K) commit = 16252928(15872K) max = 259522560(253440K) 15872K) 最大 = 259522560(253440K)

私の質問は次のとおりです。

  1. finalize() を呼び出していません。メモリリークですか?
  2. N=1000まで空きメモリに変化はありません。ただし、N=1500 の場合、プログラムの最後に使用されるメモリには 2 つの異なる値、つまり 282K と 370K があります。なぜそうなるのですか?
  3. N=6500 の場合、JVM はエラーを生成します。では、なぜ try{} の最後の 2 つのステートメントが実行されるのでしょうか。
4

5 に答える 5

1

すでにほとんどの回答は、StackOverflowErrorとメモリリークの違いを説明しています。

  • N=1000までは空きメモリに変化はありません。ただし、N = 1500の場合、プログラムの最後に使用されるメモリには282Kと370Kの2つの異なる値があります。なぜそうなのですか?

  • これは、新しいオブジェクトを作成するたびに、前のオブジェクトが到達不能になり(参照がない、参照をオーバーライドする)、必要に応じて解放できるためです。

jvmをメモリ不足にする(リークしない)ためのこれまでの最も簡単な例。

public class PrintSeries {

private static String COMMA = ",";
private StringBuilder buildStream;// = new StringBuilder();

public static void main(String[] args) {
    System.out.println(new PrintSeries().convert(10));
    System.out.println(new PrintSeries().convert(1000000000));
}

private String convert(int n) {
    buildStream = new StringBuilder();

    while (n > 1) {
        buildStream.append(n-- + COMMA);
    }
    buildStream.append(n);
    return buildStream.toString();
   }
}
  • 出力

    10,9,8,7,6,5,4,3,2,1スレッド"main"の例外java.lang.OutOfMemoryError:java.util.Arrays.copyOf(Arrays.java:2882)のJavaヒープスペースjava.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390)at java.lang.StringBuilder.append(StringBuilder.java:119)at com.cctest.algotest .string.PrintSeries.convert(PrintSeries.java:17)at com.cctest.algotest.string.PrintSeries.main(PrintSeries.java:10)

于 2012-04-25T03:52:30.370 に答える
1

finalize() を呼び出していません。メモリリークですか?

いいえ、メモリ リークはありません。常にtestObjオブジェクトへのアクセス可能な参照を保持しているためfinalize、アプリケーションで呼び出されることはありません。

アプリケーションで行うことは、巨大なオブジェクト グラフを作成することだけです。

ここでは、Java で実際のメモリ リークを作成する方法について説明します。

于 2012-04-25T03:45:29.617 に答える
1

finalize() を呼び出していません。メモリリークですか?

Finalize が呼び出されることは保証されていません。ガベージ コレクターが指定されたオブジェクトを収集するときに呼び出されますが、実行が終了する前にオブジェクトが収集されることは保証されません。

N=1000まで空きメモリに変化はありません。ただし、N=1500 の場合、プログラムの最後に使用されるメモリには 2 つの異なる値があります。つまり、282K と 370K です。なぜそうなるのですか?

ガベージコレクターの実行と実行される瞬間に依存すると思います。

N=6500 の場合、JVM はエラーを生成します。では、なぜ try{} の最後の 2 つのステートメントが実行されるのでしょうか。

これは、継承元が例外継承ブランチの一部ではなく、の兄弟であるため、例外をキャッチしていないためです。StackOverflowErrorとにかくErrorExceptionキャッチにコードがなく、try の最後の 2 つのメソッドが実行されていないためです。例外がスローされました。

要約すると、メモリリークは発生しませんでした。たとえば、到達可能なコレクションにオブジェクトを格納するなど、ある時点で実行フローから(直接または間接的に)到達可能なオブジェクトへの参照がある場合、Javaでメモリリークが発生します。 、またはシングルトン。

ガベージ コレクター自体は、プログラムからまったく到達できないオブジェクト グラフを解放するのに十分スマートです。

私はそれを明確にすることができれば幸いです。

于 2012-04-25T03:40:10.267 に答える
1

Java がそこに「ぶら下がっている」ものをすべて処理するため、プログラムが「リーク」することはありません。これがガベージコレクション言語の利点です。

しかし、あなたが持っているのは StackOverFlow エラーです。基本的に、スタック (関数のチェーンであり、その深さ) は、ヒープよりもはるかに小さいです。ヒープは、メイン メモリのサイズとほぼ同じです。各スレッドのスタックははるかに小さいです。基本的に、「深さ」を行うことでその限界に達しています。

「リーク」をテストしたい場合 (または、最終的にはリークしないという考え)、次のような方法を試してください。

public class MemoryLeakTest {
     int number=0;
     public MemoryLeakTest mobj;

     MemoryLeakTest(int num){
        number = num;
     }

     protected void finalize(){
        System.out.println(number + " released.");
     }

    public static void main(String[] args) {
        try{
             System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
             System.out.println("Free Memory in starting "+ Runtime.getRuntime().freeMemory());
             MemoryLeakTest first = new MemoryLeakTest(0);  // Keep a reference to one of them
             MemoryLeakTest current = first;
             for(int i = 1; i < Int.Parse(args[0]); i++) // forgive me, Java's been a while.  This may be C#.  But parse the first arg for your number of objects
             {
                 current.mobj = new MemoryLeakTest(i);
                 current = current.mobj;
             }
             System.out.println("Free Memory in end "+ Runtime.getRuntime().freeMemory());
             System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
        }
        catch(Exception exp){}
        finally{
            System.out.println("Free Memory"+ Runtime.getRuntime().freeMemory());
            System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
        }
    }

}

firstこれにより、範囲外になるまですべてメモリ内のオブジェクトの「チェーン」が得られます。

于 2012-04-25T03:48:31.730 に答える
0

これは、メモリ リークの証拠ではありません。StackOverflowErrorプログラムはnotをスローしていOutOfMemoryErrorます。実際には、コンストラクターが再帰的に自分自身を呼び出しており、再帰呼び出しの数が大きな数 (6,000 から 6,500 の間) を超えると、スタックスペースが不足します。

finalize() を呼び出していません。メモリリークですか?

いいえ。GC が実行されていないため、finalize() メソッドが呼び出されていない可能性があります。ヒープがいっぱいになっていないため、実行されていません。それが本当の説明ではない場合でも、finalize()メソッドが呼び出されるという保証はありません。あなたが持っている唯一の絶対的な保証は、オブジェクトのメモリがJVMによって再利用されるfinalize()に呼び出されることです。

N=1000まで空きメモリに変化はありません。ただし、N=1500 の場合、プログラムの最後に使用されるメモリには 2 つの異なる値、つまり 282K と 370K があります。なぜそうなるのですか?

なぜそうなったのかはわかりませんが、何か重要なことを示しているとは思いません。(JVM の内部では、メモリ割り当てや使用パターンなどの非決定性の原因となる可能性のあるあらゆる種類のことが発生します。)

N=6500 の場合、JVM はエラーを生成します。では、なぜ try{} の最後の 2 つのステートメントが実行されるのでしょうか。

JVM が突然終了しない限り、内のステートメントfinallyは常に実行されます。がStackOverflowErrorスローされると、他の例外と同様に伝播し、キャッチして回復することができます (場合によっては)。

于 2012-04-25T03:33:05.550 に答える