0

タブ区切り文字列の行が多いファイルから読み取る次のJAVAクラスがあります。行の例は次のようになります。

GO:0085044      GO:0085044      GO:0085044

コードは各行を読み取り、split関数を使用して3つのサブ文字列を配列に配置し、次にそれらを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());
        }
    }
}

プログラムを実行すると、しばらく実行した後、次の実行時エラーが発生しました。メモリ使用量が増え続けていることに気づきました。

スレッド「main」の例外java.lang.OutOfMemoryError:java.util.regex.Pattern。(Pattern.java:1150)のjava.util.regex.Pattern.compile(Pattern.java:1469)でGCオーバーヘッド制限を超えました。 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パラメーターを試しましたが、それは役に立ちませんでした。したがって、コードにメモリリークがあると思いますが、見つかりません。

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

4

3 に答える 3

3

メモリ リークはありません。あまりにも多くのデータを保存しようとしているだけです。2GB のテキストは、Java 文字として 4GB の RAM を必要とします。さらに、String オブジェクトのオーバーヘッドごとに約 48 バイトあります。テキストが 100 文字行であると仮定すると、さらに約 1 GB あり、合計で 5 GB になりMap.Entryます。まだオブジェクトを数えていません! データに対してこのプログラムを実行するには、控えめに言っても、少なくとも 6 GB の Java ヒープが必要です。

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

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

これらの簡単な手順が大いに役立つ場合があります。

于 2012-04-15T03:02:03.320 に答える
0

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

また、コードをそれぞれ異なる責任を持つ異なるクラスに分割することをお勧めします。一方の側に「2 つのキー マップ」クラス (TreeMap) を、もう一方の側に「パーサー」クラスを配置すると、デバッグが容易になります。 .

これは、この巨大なマップをRAM内に保存することは絶対に良い考えではありません...または、いくつかの小さなファイルでベンチマークを作成し、推定して、最悪のケースに合わせてシステムに必要なRAMを推定する必要があります.. . そして、Xmx を適切な値に設定します。Berckley DB などのKey-Value ストアを使用しない理由はありません。リレーショナル DB よりも単純で、2 レベルのインデックス作成の必要性に正確に適合するはずです。ストアの選択については、この投稿を確認してください:キー値ストアの提案

幸運を

于 2012-04-15T18:31:02.457 に答える
0

オンザフライで多くのオブジェクトが生成されるため、おそらくString.split純粋な情報を使用して保存するべきではありません。StringString

形式がかなり固定されているように見えるため、ベースのアプローチを使用してみてくださいchar。これにより、1 行のさまざまなデータ ポイントの正確なインデックスがわかります。

実験にもう少し興味がある場合は、メモリがマップされているDirectByteBufferCharBuffer、ファイルをトラバースするために使用される を使用して、NIO に基づくアプローチを使用してみてください。そこでは、さまざまなデータ ポイントのインデックスをマーカー オブジェクトにマークし、String後で必要なときに実際のデータのみをロードすることができます。

于 2012-04-15T18:50:58.030 に答える