6

私は現在、XML スキーマを扱う概念プロトタイプの証明をテストしており、ツリー オートマトン用の非常にメモリを消費する外部ライブラリ (ソースを取得しています) を中心に構築しています。「実際のピーク」(ヒープ)スキーマサイズの増加に伴うさまざまな実行のメモリ消費(使用されるメトリックは私の目的に適合し、質問には影響しません)、または少なくともその合理的な近似値。

100MB の実際のピークでの実行について、大きさのオーダーを与えるために (入力/パラメーターのまったく同じ構成を数回実行してテストし、-Xmx および -Xms を使用して jvm メモリを値の減少に強制しました。スレッド "main" java.lang.OutOfMemoryError: GC オーバーヘッド制限を超えました < 100MB, 安定した再現性のある結果) 約 1.1GB を占めます.

過去 10 日間、Web とスタックオーバーフローで質問を読んでいますが、実際に知っていることは次のとおりです。

  1. System.gc() は GC の実行を「提案」し、決して強制しないため、メモリ使用量のピークを検出するためにそれに依存することはできません

  2. 通常提案されているのは、オブジェクトの占有をカウントすることです (私はこれについてSizeOfプロジェクトを見ました。私のニーズに合わないとしても、私は試してみましたがうまくいきました)。さまざまなメソッドで多数のコレクション (セット、リスト、マップ) イテレータが呼び出され、非常に多くの回数 (私が覚えている限りでは、10 分間の実行でそれぞれ数百万回) が呼び出されるため、関連するすべてのオブジェクトを検出することは非常に困難です。合計を実行します(ボトルネックを1つだけ特定することはできませんでしたが、メモリ消費グラフを使用して数日で多くの実行をデバッグしました)

  3. メソッドのメモリ占有量 (オブジェクト メモリ割り当てのピークとして表される) を簡単に取得する方法はありません。

事実は、System.gc() 呼び出しが信頼できないことを自分で経験したことです (たとえば、同じ構成の異なる実行、GC が実際に呼び出されたかどうかに起因する System.gc() の後の異なるメモリ読み取り)。JVisualVMまたは Jconsole で「GC ボタン」を押しても、GC の実行に失敗したり、実行を拒否したりすることはありません。

だから私の質問は:そのボタンの実装を呼び出すこと(私はまだ試していませんが、これまで読んだことでは、 jconsole.jar をattach apiと一緒に使用することは可能だと思われます)は、 System.gc() を直接呼び出すこととは異なります私のコードから、私の問題を解決しますか? そうでない場合、そのボタンの「決定論的動作」をどのように説明しますか?

これまで、10 個の増加するスキーマ サイズ (この種の測定では、スキーマは単一の「複雑さパラメーター」から自動的に生成されます) を考慮して、実際のメモリ ピークの手動テストを行い、予想される曲線をプロットしました。より良い解決策を得る -Xmx/-Xms を外部 jar として実行し、予想されるメモリ ピークの予測よりもわずかに少なくして、外部プロセス ErrorStream で OutMemoryException をキャッチし、実行が完了するまでメモリを増やして再起動したいが達成された。(単純なメモリ予測が十分に堅牢でない場合は、適切な機械学習手法を適用します)。これがエレガントな解決策ではないことはわかっていますが、私のシナリオ (学界) では、これらの測定に余分な時間を費やす余裕があります。

システム情報 (マシンは Fedora 17、64 ビット):

Java バージョン "1.7.0_04" Java(TM) SE ランタイム環境 (ビルド 1.7.0_04-b20) Java HotSpot(TM) 64 ビット サーバー VM (ビルド 23.0-b21、混合モード)

前もって感謝します、 アレッサンドロ

4

4 に答える 4

4

私の知る限り、Jconsoleまたはその他のツールはSystem.gc()のみを使用します。他に選択肢はありません。誰もが知っているように、JavaはSystem.gc()に依存しないようにすべての人に指示しますが、それはそれがまったく機能しないという意味ではありません。

だからあなたの質問に来ると、どうしてそのボタンを押すとG​​Cが直接呼び出されるのか心配しているようですが、JavaはSystem.gcがGCを呼び出すことを「提案」しているだけだと言っています。つまり、そのボタンはSystem.gc()も呼び出し、JavaがGCを試行することを「提案」するだけであり、Javaがその時点でGCを実行することを決定することがあります(保証されていませんが、Javaが実行します)。

したがって、この事実を証明するために、オブジェクトのロードを作成するだけの単純なプログラムを作成しました。「System.gc()」の行にコメントがあります。ここで、最初にコメント付きのSystem.gc()を使用して、次にSystem.gc()のコメントを解除して、この同じプログラムを実行してみてください。VM引数を-verbose:gc -XX:+ PrintGCTimeStamps -XX:+PrintGCDetailsとして指定してください。

package ravi.tutorial.java.gc;

/**
 * Just to test GC. RUn with below VM arguments.
 * 
 * -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
 * 
 * 
 * @author ravi.k
 * 
 */
public class TestGC {

    public static A a;

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 100; i++) {
            populateObjects();
            System.out.println("population done for batch: " + i);
        }

    }

    public static void populateObjects() {
        for (int i = 0; i < 100000; i++) {
            a = new A("A");
        }
        //System.gc();
    }

}

class A {
    String s;

    public A(String s) {
        this.s = s;
    }
}

ここで部分的な出力が私のマシンから出ています。

Commened System.gc():ここでGCはjreの意志で呼び出されます。

population done for batch: 0
population done for batch: 1
population done for batch: 2
population done for batch: 3
population done for batch: 4
population done for batch: 5
population done for batch: 6
population done for batch: 7
population done for batch: 8
population done for batch: 9
0.332: [GC 0.332: [ParNew: 17024K->410K(19136K), 0.0024479 secs] 17024K->410K(83008K), 0.0025219 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
population done for batch: 10
population done for batch: 11
population done for batch: 12
population done for batch: 13
population done for batch: 14
population done for batch: 15
population done for batch: 16
population done for batch: 17
population done for batch: 18
population done for batch: 19
0.344: [GC 0.344: [ParNew: 17434K->592K(19136K), 0.0011238 secs] 17434K->592K(83008K), 0.0011645 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
population done for batch: 20
population done for batch: 21
population done for batch: 22
population done for batch: 23
population done for batch: 24
population done for batch: 25
population done for batch: 26
population done for batch: 27
population done for batch: 28
population done for batch: 29
population done for batch: 30
0.353: [GC 0.353: [ParNew: 17616K->543K(19136K), 0.0011398 secs] 17616K->543K(83008K), 0.0011770 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
population done for batch: 31
population done for batch: 32
population done for batch: 33

コメントなしのSystem.gc():ここでは、バッチごとにGCが呼び出されます。現在、System.gc()はGCを提案しているだけですが、Javaはその時点でGCを実行することを選択しています。これは、他のツールの魔法のGCボタンの場合とまったく同じです:)

0.337: [Full GC (System) 0.337: [CMS: 0K->400K(63872K), 0.0219250 secs] 3296K->400K(83008K), [CMS Perm : 4423K->4422K(21248K)], 0.0220152 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
population done for batch: 0
0.364: [Full GC (System) 0.364: [CMS: 400K->394K(63872K), 0.0161792 secs] 2492K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0162336 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
population done for batch: 1
0.382: [Full GC (System) 0.382: [CMS: 394K->394K(63872K), 0.0160193 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0160834 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
population done for batch: 2
0.399: [Full GC (System) 0.399: [CMS: 394K->394K(63872K), 0.0160866 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0161489 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
population done for batch: 3
0.417: [Full GC (System) 0.417: [CMS: 394K->394K(63872K), 0.0156326 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0156924 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
population done for batch: 4
0.434: [Full GC (System) 0.434: [CMS: 394K->394K(63872K), 0.0157274 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0157897 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
population done for batch: 5

さらに追加するには、スレッドと同じです。スレッドがいつ実行されるかは保証されませんが、サンプルスレッドプログラムを作成するときはいつでも、スレッドはその時間自体を実行します。したがって、スレッドが開始されるとすぐにJavaが実行された理由を非難するべきではありません:)。Javaはこれらのものに依存しないと言っているだけですが、それらは機能します。また、場合によっては機能しますが、毎回機能するわけではありません。それらのjconsoleツールでさえGCの実行に失敗する可能性がありますが、それは私たちが見たことがないということだけです。

于 2012-11-09T13:17:14.910 に答える
3

私はこの些細なアプローチでかなりの肯定的な経験をしています:

System.gc();
Thread.sleep(500);
System.gc();

多くの場合、1 回の GC 実行では、オブジェクトがファイナライズで復活する可能性があるオブジェクトのファイナライズの問題が原因で十分ではありません。したがって、追加のメモリは 2 回目の GC 実行で解放されます。

これは、一見「よりスマート」なアプローチと同様に、すべてヒューリスティックであり、GC 構成を含む JVM の正確なバージョンに大きく依存していることに注意してください。しかし、多くの場合、一般性にはあまり関心がありません。現在それが機能し、測定を行うことができるのであれば、それが進むべき道です。

于 2012-11-09T13:42:13.543 に答える
0

1) System.gc() は GC の実行を「提案」し、決して強制しないため、メモリ使用量のピークを検出するためにそれに依存することはできません

これは仕様に記載されていることですが、OpenJDK または HotSpot を使用している場合は、オフにしない限り、常にフル GC が実行されます。

通常提案されているのは、オブジェクトの占有をカウントすることです

市販のメモリ プロファイラを使用することをお勧めします。JVM を最大 8 GB で開始し、どれだけ使用しようとするかを確認します。その後、もっと欲しいか、使っていないように見えるかの判断に基づいて、私はそれを増減します.

メソッドのメモリ占有量 (オブジェクト メモリ割り当てのピークとして表される) を簡単に取得する方法はありません。

メソッドが使用する唯一のメモリはスタック上にあります。メソッドで作成したオブジェクトの数 (カウント、クラス、サイズ) を追跡できますが、それらのオブジェクトはそのメソッドに属しておらず、メソッドが戻った後でもどこでも使用できます。

そうでない場合、そのボタンの「決定論的動作」をどのように説明しますか?

私はそれを主観的な分析に落とし込みます。;)

理想的には、効率的に実行するために必要な最小メモリの 2 ~ 3 倍のメモリで JVM を実行する必要があります。1 ドル未満の 100 MB を数個節約しようとしても、必ずしも役立つとは限りません。;)

于 2012-11-09T10:12:03.273 に答える