1

ファイルからシーケンスの大きなリストを読み取り、そのリスト内のすべてのペア間で計算を行うプログラムがあります。次に、これらすべての計算をハッシュセットに格納します。このプログラムを途中で実行すると、GC オーバーヘッド制限エラーが発生します。

これは、ガベージ コレクターが計算時間の 98% を使い果たしており、ヒープの 2% を回復することさえできないためです。ここに私が持っているコードがあります:

ArrayList<String> c = loadSequences("file.txt"); // Loads 60 char DNA sequences
HashSet<DNAPair,Double> LSA = new HashSet<DNAPair,Double>(); 
for(int i = 0; i < c.size(); i++) {
    for(int j = i+1; j < c.size(); j++) {
        LSA.put(new DNAPair(c.get(i),c.get(j)),localSeqAlignmentSimilarity(c.get(i),c.get(j)));
    }
}

実際のメソッドのコードは次のとおりです。

public static double localSeqAlignmentSimilarity(String s1, String s2) {
    s1 = " " + s1;
    s2 = " " + s2;
    int max = 0,h = 0,maxI = 0,maxJ = 0;

    int[][] score = new int[61][61];
    int[][] pointers = new int[61][61];

    for(int i = 1; i < s1.length(); i++) {
        pointers[i][0] = 2;
    }
    for(int i = 1; i < s2.length(); i++) {
        pointers[0][i] = 1;
    }

    boolean inGap = false;
    for(int i = 1; i < s1.length(); i++) {
        for(int j = 1; j < s2.length();  j++) {
            h = -99;
            if(score[i-1][j-1] + match(s1.charAt(i),s2.charAt(j)) > h) {
                h = score[i-1][j-1] + match(s1.charAt(i),s2.charAt(j));
                pointers[i][j] = 3;
                inGap = false;
            } 
            if(!inGap) {
                if(score[i-1][j] + GAPPENALTY > h) {
                    h = score[i-1][j] + GAPPENALTY;
                    pointers[i][j] = 2;
                    inGap = true;
                } 
                if(score[i][j-1] + GAPPENALTY > h) {
                    h = score[i][j-1] + GAPPENALTY;
                    pointers[i][j] = 1;
                    inGap = true;
                }
            } else {
                if(score[i-1][j] + GAPEXTENSION > h) {
                    h = score[i-1][j] + GAPEXTENSION;
                    pointers[i][j] = 2;
                    inGap = true;
                } 
                if(score[i][j-1] + GAPEXTENSION > h) {
                    h = score[i][j-1] + GAPEXTENSION;
                    pointers[i][j] = 1;
                    inGap = true;
                }
            }

            if(0 > h) h = 0;

            score[i][j] = h;
            if(h >= max) {
                max = h;
                maxI = i;
                maxJ = j;
            }
        }
    }

    double matches = 0;
    String o1 = "",  o2 = "";
    while(!(maxI == 0 && maxJ == 0)) {
        if(pointers[maxI][maxJ] == 3) {
            o1 += s1.charAt(maxI);
            o2 += s2.charAt(maxJ);
            maxI--;
            maxJ--;
        } else if(pointers[maxI][maxJ] == 2) {
            o1 += s1.charAt(maxI);
            o2 += "_";
            maxI--;
        } else if(pointers[maxI][maxJ] == 1) {
            o1 += "_";
            o2 += s2.charAt(maxJ);
            maxJ--;
        }
    }

    StringBuilder a = new StringBuilder(o1);
    b = new StringBuilder(o2);
    o1 = a.reverse().toString();
    o2 = b.reverse().toString();
    a.setLength(0);
    b.setLength(0);

    for(int i = 0; i < Math.min(o1.length(), o2.length()); i++) {
        if(o1.charAt(i) == o2.charAt(i)) matches++;
    }
    return matches/Math.min(o1.length(), o2.length());
}

これは、メソッド内で宣言するすべての変数 (2 つの int 配列と stringbuilders など) が、メソッドが実行されるたびにますます多くのオブジェクトを作成するためだと考えたので、それらをすべて静的フィールドに変更し、毎回クリアしました ( ex. Arrays.fill(score,0);) 新しいオブジェクトを作成する代わりに。

ただし、これはまったく役に立ちませんでした。それでも同じエラーが発生しました。

すべての計算を格納するハッシュセットが大きくなりすぎて、Java で格納できなくなっている可能性がありますか? ヒープ領域不足エラーが発生していないので、ちょっと奇妙に思えます。

また、コマンド ライン引数を変更して JVM により多くのスペースを与えましたが、それは役に立たなかったようです。

この問題に関する洞察は役に立ちます。ありがとう!

4

2 に答える 2

1

c.size() が 73657 で、シーケンスが一意である場合、これは問題です。

HashSet<DNAPair,Double> LSA = new HashSet<DNAPair,Double>(); 
 for(int i = 0; i < c.size(); i++) {
   for(int j = i+1; j < c.size(); j++) {
      LSA.put(...);
   }
 }

これらが一意のシーケンスであると仮定すると、基本的に各ペアの LSA に要素を追加します。あなたは70kシーケンスを持っていると述べているので、70k * 70k =〜50億のペアがあり、それぞれが保存するのに最小4バイトかかります。つまり、これには最低でも20 + GBを割り当てる必要があります実行可能であること。

于 2013-08-19T23:04:44.977 に答える
0

はい、確かに、データの量が大きすぎてメモリに保存できない可能性があります。JConsoleなどを使用してプログラムを実行している間、または一般的にプログラム内から MemoryMXBean から読み取りを行っている間に、プログラムのメモリ使用量を実際にプロファイリングすることから始めます。

役に立つ場合に備えて、Java プログラム内から Java オブジェクト (およびサブオブジェクト) の実際のメモリ使用量を照会できる小さなClassmexerエージェントを作成しました。

ちなみに、実際には本来静的であってはならないオブジェクトを静的にするなどして、JVM のメモリ管理システムを「ごまかす」または横取りしようとすることは、通常は有益ではありません。

于 2013-08-19T22:46:54.440 に答える