2012-04-15 13 views
0

多くのタブ区切りの文字列を含むファイルから読み込むには、次のJAVAクラスがあります。例えば、行は以下のようである:java.lang.OutOfMemoryErrorを使用したJAVA処理ファイル:GCオーバーヘッドの上限を超えたエラー

GO:0085044  GO:0085044  GO:0085044 

コードが各行を読み取り、配列に三つのサブ文字列を入れて、分割機能を使用し、それは2つのレベルハッシュにそれらを置きます。

public class LCAReader { 
    public static void main(String[] args) { 
     Map<String, Map<String, String>> termPairLCA = new HashMap<String, Map<String, String>>(); 
     File ifile = new File("LCA1.txt"); 
     try { 
      BufferedReader reader = new BufferedReader(new FileReader(ifile)); 
      String line = null; 
      while((line=reader.readLine()) != null) { 
       String[] arr = line.split("\t"); 
       if(termPairLCA.containsKey(arr[0])) { 
        if(termPairLCA.get(arr[0]).containsKey(arr[1])) { 
         System.out.println("Error: Duplicate term in LCACache"); 
        } else { 
         termPairLCA.get(arr[0]).put(new String(arr[1]), new String(arr[2])); 
        } 
       } else { 
        Map<String, String> tempMap = new HashMap<String, String>(); 
        tempMap.put(new String(arr[1]), new String(arr[2])); 
        termPairLCA.put(new String(arr[0]), tempMap); 
       } 
      } 
      reader.close(); 
     } catch (IOException e) { 
      System.out.println(e.getMessage()); 
     } 
    } 
} 

私がプログラムを実行したとき、私は実行時に次のエラーが発生しました。私はメモリ使用量が増加し続けていることに気づいた。スレッド内

例外 "メイン" java.lang.OutOfMemoryErrorを:GCオーバーヘッドの制限はjava.util.regex.Patternのにjava.util.regex.Pattern.compile(Pattern.java:1469) にを超えて(パターン。 .java:1150) at java.util.regex.Pattern.compile(Pattern.java:840) at java.lang.String.split(String.java:2304) at java.lang.String.split(String .java:2346) at LCAReader.main(LCAReader.java:17)

入力ファイルはほぼ2Gで、マシンを実行すると8Gのメモリがあります。私はまた、プログラムを実行するために-Xmx4096mパラメータを試しましたが、それは助けになりませんでした。だから私はコード内にいくつかのメモリリークがあると思いますが、私はそれらを見つけることができません。

誰もが私にこれを助けることができますか?前もって感謝します!

答えて

3

メモリリークはありません。あまりにも多くのデータを格納しようとしているだけです。 2GBのテキストはJava文字として4GBのRAMを使用します。さらに、Stringオブジェクトのオーバーヘッドあたり約48バイトがあります。テキストが100文字の文字列であると仮定すると、別のGBが約5GBありますが、まだMap.Entry個のオブジェクトを数えていません。あなたのデータでこのプログラムを実行するには、最低でも保守的に6GBのJavaヒープが必要です。

これを改善するために簡単にできることがいくつかあります。まず、new String()コンストラクタを失ってしまいます。それらは役に立たず、ガベージコレクタの作業をもっと難しくします。文字列は不変なので、コピーする必要はありません。次に、インラインプールを使用して重複した文字列を共有することができます。これは、データが実際にどのように見えるかによって異なります。しかし、たとえば、

tempMap.put(arr[1].intern(), arr[2].intern()); 

これらの簡単な手順は、多くの役に立つ可能性があります。

+2

'String.split()'を使用しているときには、 'new String()'を慎重に注意してください。 'String.split()'の結果から少数のトークンだけが必要な場合、 'String.split()'によって返される 'Strings'は単純にラッパーであるため、' new String() 'を使うのは良い考えです。分割された文字列全体を指します。だから、もしあなたが望むものがいくつかあったとしても、ヒープに文字列全体を残すことになります( 'String'ではなく' \ t'セパレータ)。 – ulmangt

+0

私はintern()メソッドを試して、ヒープサイズを6Gに増やしました。例外は表示されません。メモリ使用量から、私はそれがまだ多くのメモリを使用することがわかります。ヒープサイズの増加は多くの助けになり、internメソッドはほとんど役に立たなかった。ありがとう! – Wei

0

リークは見られません。地図を保存するには非常に膨大なメモリが必要です。 XX:+ HeapDumpOnOutOfMemoryErrorオプションを使用してヒープダ​​ンプを作成し、それをスタンドアローンバージョンのEclipse Memory Analyzerにインポートすることができます。それはガベージコレクタがその仕事をするのを妨げる可能性がある最大の保持されたオブジェクトと参照ツリーを示すことができます。 さらに、Netbeans Profilerのようなプロファイラは、(StringとCharインスタンスの数を確認するなどの)興味深いリアルタイム情報をたくさん提供します。

コードを分割して、責任の異なる別のクラスにすることもお勧めします。片方の "2つのキーマップ"クラス(TreeMap)と反対側の "パーサ"クラスは、デバッグが容易...

これは間違いなく、この巨大なマップをRAMに保存するための良い考えではありません...ベンチマークをいくつかのより小さいファイルで作成し、あなたのシステムに必要な推定RAMを得るために外挿する必要があります。そして、Xmxを適切な値に設定してください。 Berckley DBのようなキーバリューストアを使用しないでください。リレーショナルDBよりも簡単で、2レベルのインデックス作成が必要です。店の選択のための チェックこの記事を:key-value store suggestion

幸運

0

これはその場でStringオブジェクトの多くを生成してあなたは、おそらくString.splitを使用して、純粋なStringなどの情報を保存しないでください。

charベースのアプローチを使用してみてください。形式が固定されているように見えるので、1行でさまざまなデータポイントの正確なサイズを知ることができます。

もう少し実験をおこなうなら、ファイルをトラバースするために使用されるDirectByteBufferまたはCharBufferのメモリマップを使用してNIO支援アプローチを使用できます。そこでは、さまざまなデータポイントの指標をMarkerオブジェクトにマークし、必要に応じて後で実際のStringデータをプロセスにロードするだけです。

関連する問題