16

HashMapのサブクラスである内部クラスを使用しています。私はStringキーとdouble[]値としてを持っています。1つあたり約200のダブルを保存しdouble[]ます。キー、ポインター、およびダブルを格納するために約700MBを使用する必要があります。ただし、メモリ分析では、それよりもはるかに多く(2 GB強)が必要であることがわかりました。

TIJmp (プロファイリングツール)を使用すると、メモリ全体のほぼ半分を使用していることがわかりました。char[]TIJmpはそれがとchar[]から来たSerializableと言ったCloneable。その中の値は、フォントとデフォルトのパスのリストからメッセージと単一の文字までの範囲でした。

SerializableJVMでの正確な動作は何ですか?したがって、常に「永続的な」コピーを保持し、メモリフットプリントのサイズを2倍にしますか?JVMをメモリを大量に消費することなく、実行時にオブジェクトのバイナリコピーを書き込むにはどうすればよいですか?

PS:メモリ消費量が最も増える方法は以下のとおりです。このファイルには、約229,000行と1行あたり202フィールドがあります。

public void readThetas(String filename) throws Exception
{
    long t1 = System.currentTimeMillis();
    documents = new HashMapX<String,double[]>(); //Document names to indices.
    Scanner s = new Scanner(new File(filename));
    int docIndex = 0;
    if (s.hasNextLine())
        System.out.println(s.nextLine()); // Consume useless first line :)
    while(s.hasNextLine())
    {
        String[] fields = s.nextLine().split("\\s+");
        String docName = fields[1];
        numTopics = fields.length/2-1;
        double[] thetas = new double[numTopics];
        for (int i=2;i<numTopics;i=i+2)
            thetas[Integer.valueOf(fields[i].trim())] = Double.valueOf(fields[i+1].trim());
        documents.put(docName,thetas);
        docIndex++;
        if (docIndex%10000==0)
            System.out.print("*"); //progress bar ;)
    }
    s.close();
    long t2 = System.currentTimeMillis();
    System.out.println("\nRead file in "+ (t2-t1) +" ms");
}

Oh !、そしてHashMapXは次のように宣言された内部クラスです:

public static class HashMapX< K, V> extends HashMap<K,V> {
    public V get(Object key, V altVal) {
        if (this.containsKey(key))
            return this.get(key);
        else
            return altVal;
    }
}
4

2 に答える 2

5

これはすべての質問に対応できるわけではありませんが、シリアル化によってメモリ使用量を大幅に増やすことができる方法です:http: //java.sun.com/javase/technologies/core/basic/serializationFAQ.jsp#OutOfMemoryError

つまり、開いたままにしておくと、そのメソッドObjectOutputStreamを明示的に呼び出さない限り、そこに書き込まれたオブジェクトをガベージコレクションすることはできません。reset()

于 2011-04-19T17:54:45.210 に答える
4

だから、私は答えを見つけました。これは私のコードのメモリリークです。SerializableまたはCloneableとは何の関係もありませんでした。

このコードはファイルを解析しようとしています。各行には、抽出しようとしている値のセットが含まれています。次に、これらの値の一部を保持し、HashMapXまたはその他の構造体に格納します。

問題の核心はここにあります:

        String[] fields = s.nextLine().split("\\s+");
        String docName = fields[1];

そして私はそれをここに伝播します:

        documents.put(docName,thetas);

docNameは配列(フィールド)内の要素への参照であり、プログラムの存続期間中はその参照を保持します(グローバルなHashMapドキュメントに格納することにより)。その参照を存続させている限り、String[]フィールド全体をガベージコレクションすることはできません。ソリューション:

        String docName = new String(fields[1]); // A copy, not a reference.

したがって、オブジェクトをコピーして、配列要素への参照を解放します。このようにして、すべてのフィールドを処理すると、ガベージコレクターは配列によって使用されているメモリを解放できます。

これが、splitを使用して大きなテキストファイルを解析し、一部のフィールドをグローバル変数に格納するすべての人に役立つことを願っています。

コメントありがとうございます。彼らは私を正しい方向に導いてくれました。

于 2011-04-21T15:12:07.657 に答える