2012-02-25 11 views
3

私はJavaプログラムを実装しました。これは基本的に固定数のスレッドを持つマルチスレッドサービスです。各スレッドは、一度に1つのタスクを取り、ハッシュセットを作成します。ハッシュセットのサイズは、1つのハッシュセット内の10から20,000以上のアイテムまでさまざまです。各スレッドの終わりに、結果はsynchronizedを使用して共有コレクションリストに追加されます。メモリ不足:ハッシュセットを使用したマルチスレッド

問題が発生するのは、ある時点でメモリ例外から抜け出すことです。今少しの研究をした後、私はこのメモリ例外がGCがメモリをクリアしているときに発生し、その時点で全世界が何かを実行するのを止めることを発見しました。

このように大量のデータを処理する方法を教えてください。ハッシュセットは使用する正しいデータ構造ですか?どのようにメモリ例外に対処するには、私は1つの方法はSystem.GC()を使用することです、それは全体のプロセスを遅くするので、再び良くないです。または、それを共有コレクションリストに追加した後に "HashSet hsN"を処分することは可能ですか?

私があなたの考えを知って、私が間違っているところを教えてください。このサービスは、膨大なデータ処理に対処する予定です。

おかげで、このような実装では

//business object - to save the result of thread execution 

public class Location{ 

    integer taskIndex; 
    HashSet<Integer> hsN; 
} 



//task to be performed by each thread 


public class MyTask implements Runnable { 


    MyTask(long task) { 
     this.task = task; 
    } 

    @Override 
    public void run() { 
       HashSet<Integer> hsN = GiveMeResult(task);//some function calling which returns a collection of integer where the size vary from 10 to 20000 

     synchronized (locations) { 
      locations.add(task,hsN); 
     } 
    } 
} 


public class Main { 

    private static final int NTHREDS = 8; 
    private static List<Location> locations; 

    public static void main(String[] args) { 
     ExecutorService executor = Executors.newFixedThreadPool(NTHREDS); 
     for (int i = 0; i < 216000; i++) { 
      Runnable worker = new MyTask(i); 
      executor.execute(worker); 
     } 
     // This will make the executor accept no new threads 
     // and finish all existing threads in the queue 
     executor.shutdown(); 
     // Wait until all threads are finish 
     while (!executor.isTerminated()) { 

     } 
     System.out.println("Finished all threads"); 
    } 
} 

JAVAが最良の選択やC#.net4のですか?

+0

このような実装では、JAVAは最良の選択ですか、C#.net4のために用意しますか? – user1213831

答えて

5

私が見ることができる問題のカップル:

  • あなたが実行するたびに個別に作成されたオブジェクトMyTask、上で同期させます。できれば変更対象の共有オブジェクト、つまりlocationsオブジェクトで同期する必要があります。 Integerオブジェクトごとに12バイトの最小値を乗じ言う万個の返されたオブジェクトそれぞれ乗じ

  • 216,000ランは、約24 ギガバイトのメモリのあります。 JVMで利用できるだけでなく、あなたのコンピュータ上で利用可能な物理メモリもたくさんありますか?

    32ビットJVMのヒープサイズ制限は2 GB未満です。一方、64ビットJVMの場合、Integerオブジェクトには約16バイトが必要となり、メモリ要件が30 GBを超えるようになります。これらの数字で

    それはあなたがOutOfMemoryError ...

    PSを得ることはほとんど驚きだ:あなたはあなたは、あなたが正しいことをやっていると思いますまだそのくらいの物理メモリが利用できる持っていてください場合tuning the JVM heap sizeをご覧ください。

EDIT:JVMに使用可能であってもメモリの25ギガバイトと

は、まだそれを押すことができる:

  • Integer目的は、現代の64ビットのJVM上の16バイトを必要とします。

  • またかかわらず、使用しているList実装の、それを指すようになり、8バイト参照を必要としています。

  • リンクリストの実装を使用している場合、各エントリのリストエントリオブジェクトのオーバーヘッドも少なくとも24バイトになります。

せいぜいあなたは25ギガバイトでは約10億Integerオブジェクトを格納するために願っています可能性 - 半分リンクされたリストを使用している場合、ということ。これは、各タスクがエラーを発生させることなく、平均して5,000個を超えるオブジェクト(それぞれ2,500個)を生成できないことを意味します。

あなたの正確な要件は不明ですが、よりコンパクトなオブジェクトを返却することを検討しましたか?例えば、それぞれHashSetから生成されたint[]配列は、オブジェクトコンテナのオーバーヘッドなしで、結果ごとに最小4バイトしか保持しません。

EDIT 2:

私はちょうどあなたがリストにHashSetオブジェクト自体を格納していることに気づきました。 HashSetオブジェクトは内部でHashMapを使用し、次に各エントリのHashMap.Entryオブジェクトを使用します。 - 8バイト

  • Integerオブジェクトを指すキー参照:64ビットJVM上のエントリオブジェクトは、格納されたオブジェクトに加えて、メモリの約40バイトを消費します。

  • 値の参照(HashSetでは常にnull) - 8バイト。

  • 次のエントリ参照 - 8バイト。

  • ハッシュ値 - 4バイト。

  • オブジェクトオーバーヘッド - 8バイト。

  • オブジェクトの埋め込み - 4バイト。

e.e.e. Integerオブジェクトごとに、HashSetに56バイトのストレージが必要です。標準的なHashMapの負荷係数が0.75の場合は、配列参照のHashMapに別の10またはバイトを追加する必要があります。 Integerごとに66バイトを使用すると、アプリケーションの残りの部分を考慮せずに、約400,000,000のオブジェクトを25 GBに保存することしかできません。これは、タスクごとに2,000未満のオブジェクトです...

EDIT 3:

代わりHashSetint[]配列をソートを格納したほうが良いでしょう。その配列は、任意の整数に対して対数時間で検索可能であり、メモリー消費量を数当たり4バイトに最小化します。メモリI/Oを考慮すると、HashSetの実装と同じ速さ(または高速)になります。

+0

私は30 GBの物理メモリを持っています。私は8GBから16GBに調整しようとしましたが、-Xmrを使用して25GBでしたが、成功しませんでした。私は共有オブジェクトで同期を使用している、私はそれが私の側に入力ミスを修正しました。私の理解は、各スレッドの実行終了時にGCのローカルのHashSet を削除候補としてマークしましたが、実際にはメモリから削除されませんでした。 GCはメモリをクリアしようとしますが、削除するメモリにはこのようなHashSet が多数存在するため、GCは処理を停止/ハングします。 theadの実行が終了する直前にHashSetを処理する方法はありますか? – user1213831

+0

@ user1213831:どのような種類のコレクションが 'locations'であり、エラー時のサイズの見積もりはありますか? – thkala

+0

詳細をお寄せいただきありがとうございます。私は、HashSetの代わりにint []を使用して結果を分析しようとします。結果を共有します。ありがとうございました – user1213831

0

おそらく、ヒープのサイズを大きくする必要があります。-Xmx JVMの設定を見てください。

1

私はTIntHashSetまたはソートされたint[]を使用してよりメモリ効率的なソリューションをしたい場合。この場合、OutOfMemoryErrorの前にFull GCが得られます。これらは問題の原因ではなく、症状です。問題の原因は、最大ヒープとして許容している量のために多すぎるメモリを使用していることです。

別の解決策は、すべてのタスクを事前に作成する代わりに、タスクを作成することです。代わりにタスクをNTHREADタスクに分割することでこれを行うことができます。すべてのソリューションを保持しようとしているようです。もしそうなら、これはあまり役に立ちません。代わりに、消費を減らす方法を見つける必要があります。

数値の分布に応じて、BitSetがより効率的になる場合があります。これはある範囲内の整数あたり1ビットを使用します。例えばあなたの範囲は0〜20,000と言います。これはわずか2.5 KBを使用します。

+0

したがって、HashSet の代わりに、HashSet を使用する場合、各要素は整数の値になります..itは1ビットしか消費しませんか? – user1213831

+0

いいえ、 'HashSet 'の代わりに、 'BitSet'を使用します。可能な値ごとにビットを使用します。 –

1

は今、研究のビットをやった後、私は、GCがメモリをクリアビジー状態であると、その時点で、それが何かを実行するために 全世界を停止したときに、このメモリ例外 が発生することがわかりました。

いいえ - 該当しません。プログラムに割り当てられたメモリより多くのメモリを使用しているため、メモリ例外が発生します。非常にまれにGCの動作に起因するメモリ例外です。このになります.GCを貧弱に設定すると起こります。

より大きい - Xmx値で実行しようとしましたか?そして、場所にはHashtableを使ってみませんか?

+0

はい-Xmxを使って8〜16GBのメモリを割り当てました。しかし、HashSetの価値はまだまだ大きくなる可能性があります。私がHashSetを使用しているのは、後の段階ですべてのスレッドが完了したからです。共有オブジェクトを繰り返し処理する必要があり、各反復でハッシュセットに特定の値を持つ要素が含まれているかどうかをチェックする必要があります。そしてこれのために、HashSet.Containsの使用はより高速です。私が間違っているなら、私を訂正してください。 – user1213831

1
  • 216000 * 10000整数をメモリに保存する場合は、膨大なメモリが必要です。
  • Xmxの設定をシステムで最大限試してみて、メモリが足りなくなるまでに保存できるオブジェクトの数を確認できます。
  • 非常に多くのスレッドの処理結果を保存する理由は明確ではありませんが、次のステップは何ですか?大量のデータを格納する必要がある場合は、おそらくデータベースを使用する必要があります。
+0

私はDBを使うことを提案しようとしていましたが、最初にそれを述べました。 +1 –