3

ここに私のコードがあります:

 public void mapTrace(String Path) throws FileNotFoundException, IOException {
    FileReader arq = new FileReader(new File(Path));
    BufferedReader leitor = new BufferedReader(arq, 41943040);
    Integer page;
    String std;
    Integer position = 0;

    while ((std = leitor.readLine()) != null) {
        position++;
        page = Integer.parseInt(std, 16);
        LinkedList<Integer> values = map.get(page);
        if (values == null) {
            values = new LinkedList<>();
            map.put(page, values);
        }
        values.add(position);
    }

    for (LinkedList<Integer> referenceList : map.values()) { 
        Collections.reverse(referenceList); 
    }

}

これは HashMap 構造です

       Map<Integer, LinkedList<Integer>> map = new HashMap<>();

50mb ~ 100mb のトレース ファイルの場合は問題ありませんが、より大きなファイルの場合は次のようになります。

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: GC overhead limit exceeded

逆メソッドがメモリ使用量を増やしているかどうか、LinkedList が他のリスト構造よりも多くのスペースを使用しているかどうか、またはリストをマップに追加する方法が必要以上のスペースを使用しているかどうかはわかりません。何がそんなに多くのスペースを使用しているのか、誰か教えてもらえますか?

4

3 に答える 3

3

何がそんなに多くのスペースを使用しているのか、誰か教えてもらえますか?

簡単に言えば、スペースを使用しているのは、選択したデータ構造のスペース オーバーヘッドである可能性が高いということです。

  1. 私の推測ではLinkedList<Integer>、64 ビット JVM では、整数自体を含むリスト内の整数ごとに約 48 バイトのストレージが使用されます。

  2. 私の推測では、64 ビット マシンでは、キー オブジェクトと値オブジェクトを表すために必要なスペースを除いMap<?, ?>て、エントリごとに 48 バイトの領域が使用されます。

さて、あなたのトレース サイズの見積もりは漠然としていて数字を差し込むことができませんが、1.5Gb のトレース ファイルには 2Gb 以上のヒープが必要になると思います。


あなたが提供した数値を考えると、合理的な経験則は、現在使用しているデータ構造を使用して、トレース ファイルがヒープ メモリ内のファイル サイズの約 10 倍を占めることです。

利用可能な物理 RAM よりも多くのメモリを使用しようとするように JVM を構成する必要はありません。そうしないと、マシンがスラッシング状態に陥る可能性があり、オペレーティング システムがプロセスを強制終了し始める可能性があります。したがって、8Gb マシンの場合、-Xmx8g を超えることはお勧めしません。

まとめると、8Gb のマシンでは 600Mb のトレース ファイルに対処できるはずですが (私の見積もりが正しいと仮定して)、1.5Gb のトレース ファイルは実現不可能です。本当に大きなトレース ファイルを処理する必要がある場合は、次のいずれかを行うことをお勧めします。

  • メモリをより効率的に使用する特定のユース ケース向けのカスタム コレクション型を設計および実装する。

  • トレース ファイル全体をメモリに保持する必要がないように、アルゴリズムを再考するか、または

  • より大きなマシンを入手してください。


あなたのコメントを読む前にいくつかのテストを行いました。

この-Xmx14gオプションは、最大ヒープ サイズを設定します。観察された動作に基づいて、JVM はそれほど多くのメモリを必要とせず、OS から要求しなかったと思います。また、タスク マネージャーでメモリ使用量を見たことがあれば、それと一致する数値を見たことがあると思います。

次に、-Xmx18g を入れて 1.5GB ファイルを処理しようとしましたが、約 20 分間実行されていました。タスク マネージャーのメモリが 7.80 から 7.90 に増えています。私はこれが終わるのだろうか、どうすれば私が持っているよりも多くのメモリを使うことができるでしょうか? HDを仮想メモリとして使用しますか?

はい、それはそれがすることです。

はい、プロセスの仮想アドレス空間の各ページは、ハードディスク上のページに対応しています。

物理メモリ ページより多くの仮想ページがある場合、それらの仮想メモリ ページの一部は常にディスク上にのみ存在します。アプリケーションがこれらの非常駐ページの 1 つを使用しようとすると、VM ハードウェアが割り込みを生成し、オペレーティング システムが未使用のページを見つけてディスク コピーからデータを取り込み、制御をプログラムに戻します。ただし、アプリケーションがビジー状態の場合は、別のページを削除しその物理メモリ ページを作成する必要があります。そして、それには、追い出されたページの内容をディスクに書き込むことが含まれていた可能性があります。

最終的な結果として、物理メモリよりもはるかに多くの仮想アドレス ページを使用しようとすると、アプリケーションは多くの割り込みを生成し、その結果、ディスクの読み取りと書き込みが大量に発生します。これはスラッシングと呼ばれます。システムのスラッシングがひどすぎると、システムはほとんどの時間をディスクの読み取りと書き込みが完了するのを待つことに費やされ、パフォーマンスが劇的に低下します。また、一部のオペレーティング システムでは、OS がプロセスを強制終了して問題を「修正」しようとします。

于 2012-08-28T02:12:30.793 に答える
0

この構造を構築するには、これらのデータをberkeleydbforjavaのようなキー/値データストアに配置します。

peusdo-コード

putData(db,page,value)
 {
 Entry key=new Entry();
 Entry data=new Entry();
 List<Integer> L=new LinkedList<Integer>();;
 IntegerBinding.intToEntry(page,key);
 if(db.get(key,data)==OperationStatus.SUCCESS)
    {
    TupleInput t=new TupleInput(data);
    int n=t.readInt();

    for(i=0;i< n;++n) L.add(n);
    }

  L.add(value);
  TupleOutput out=new TupleOutput();
  out.writeInt(L.size());

  for(int v: L)  out.writeInt(v);
  data=new Entry(out.toByteArray());
  db.put(key,data);
 }
于 2012-08-28T10:07:24.560 に答える
0

スティーブンの非常に合理的な答えに加えて、すべてに限界があり、コードは単純にスケーラブルではありません。

入力が「大きい」場合(あなたの場合のように)、唯一の合理的なアプローチはストリームベースのアプローチです。これは(通常)書き込みがより複雑ですが、メモリ/リソースをほとんど使用しません。基本的に、現在のタスクを処理するために必要なものだけをメモリに保持し、できるだけ早く解放します。

awkUnix コマンド ライン ツールが最良の武器であることがわかるかもしれません。おそらく、sed、などを組み合わせて使用​​してgrep、生データを使用可能な「最終フォーマット」に変換します。


私は以前、同僚が XML を読み込んで解析し、データベースに挿入ステートメントを発行する Java プログラムを作成するのを止めました。一連のパイプ コマンドを使用して実行可能な SQL を生成し、データベース コマンド ライン ツールに直接パイプする方法を彼に示しました。 . 完成まで30分ほどかかりましたが、無事完成。また、ファイルは巨大だったので、Java では SAC パーサーと JDBC が必要で、面白くありませんでした。

于 2012-08-28T04:25:43.280 に答える