2

大量のデータ (csv など) をロードするアプリケーションを開発しています。

読み取ったセルを作成List<List<SimpleCell>>してロードしています。SimpleCell クラスには 5 つの * が含まれString、それぞれString平均 10 文字です。

SimpleCellしたがって、1000*160=160 000のインスタンスを与える 1000 行 (それぞれが 160 列を含む) を読み取ると、約 160 000 * sizeof(SimpleCell.class)=~ 160 000 * 10 * 5 = 8 000 000になると考えています。バイト =~ 7.63 MB。

しかし、jconsole を見ているとき (そして をクリックした後Perform GC) のメモリ使用量は約 790MB です。これはどうやってできるの?

「一時的な」オブジェクトへの参照は保存しないことに注意してください。メモリ使用量が上昇したときのコードは次のとおりです。

        for(int i = r.getFromIndex(); i <= r.getToIndex(); ++i) {
            System.out.println("Processing: 'ZZ " + i + "'");
            List<SimpleCell> values = saxRead("ZT/ZZ " + i + "");
            rows.add(values);
        }

saxReadinputStream を作成するだけで、それを SAX で解析し、ストリームを閉じて、セル (SAXHandler によって作成) を返します。つまり、ローカル変数しかありません (近い将来に破棄されると思います)。

out of heap error1000 行を読み取ったときに取得していますが、約 7k を読み取る必要があります。

明らかに-jvmメモリについて私が知らないことがあります。では、この比較的少量のデータをロードするときにメモリ使用量が非常に大きくなるのはなぜでしょうか?

4

4 に答える 4

3

文字列は48バイトとテキストのサイズ*2を使用します。(各文字は2バイトです)Simple Cellオブジェクトは40バイトを使用し、それらのリストは1064バイトを使用します。

これは、各行が1064 + 160 * 40 + 5 * 180 *(48 + 20)バイトまたは約68Kを使用することを意味します。1000行ある場合は、約70 MBを使用しますが、これは表示されているものよりはるかに少なくなります。

メモリプロファイルを使用して、何によってどのくらいのメモリが使用されているかを正確に確認することをお勧めします。例:VisualVMまたはYourKit。

文字列の作成方法によっては、これよりも多くのメモリを保持します。たとえば、元のXMLへの参照を保持している可能性があります。これは、元のXMLを取得したときにsubstring、実際には元のXMLのコピーを保持しているためです。


このクラスが役立つ場合があります。文字列が必要以上に使用している場合に使用するメモリの量を減らし、固定サイズのキャッシュを使用して重複を減らします。

static class StringCache {
    final WeakReference<String>[] strings;
    final int mask;

    @SuppressWarnings("unchecked")
    StringCache(int size) {
        int size2 = 128;
        while (size2 < size)
            size2 *= 2;
        strings = new WeakReference[size2];
        mask = size2 - 1;
    }

    public String intern(String text) {
        if (text.length() == 0) return "";

        int hash = text.hashCode() & mask;
        WeakReference<String> wrs = strings[hash];
        if (wrs != null) {
            String ret = wrs.get();
            if (text.equals(ret))
                return ret;
        }
        String ret = new String(text);
        strings[hash] = new WeakReference<String>(ret);
        return ret;
    }
}
于 2012-09-19T19:26:50.250 に答える
2

JVMメモリ管理は、多くのオーバーヘッドをもたらします。たとえば、32ビット仮想マシンでは、5文字の文字列は58バイトのメモリを消費します(5文字だけではありません!)。

JVMオーバーヘッド:16b +簿記フィールド:12b + char []へのポインター:4b + char [] jvmオーバーヘッド:16b +データ:10b

于 2012-09-19T19:25:48.473 に答える
2

VisualVMを使用してヒープの使用状況をプロファイリングし、驚かれる準備をします。

于 2012-09-19T19:26:44.403 に答える
1

Java は非常にメモリを消費します。次の見積もりを検討してください。

32 ビット VM:

紐のサイズ(約)

10 UTF-16 文字 = 20 バイト

1 配列の長さ = 4 バイト

1 配列オブジェクト ヘッダー = 8 バイト

1 配列参照 = 4 バイト

1 オフセット、カウント、ハッシュコード (内部フィールド) = 12 バイト

1 オブジェクト ヘッダー = 8 バイト

典型的な Java 文字列の 1 = 20 + 4 + 8 + 4 + 12 + 8 = 56 バイト

単純なセルのサイズ (およそ、文字列を含む)

5 文字列 = 56 * 5 = 280 バイト

5 文字列参照 = 5 * 4 バイト = 20 バイト

1 オブジェクト ヘッダー = 8 バイト

1 SimpleCell = 180 + 20 + 8 = 308 バイト

160000 SimpleCell = 308 * 160000 = 49280000 バイト

64 ビット VM (圧縮 oops なし)

紐のサイズ(約)

10 UTF-16 文字 = 20 バイト

1 配列の長さ = 4 バイト

1 配列オブジェクト ヘッダー = 8 バイト

1 つの配列参照 = 8 バイト

1 オフセット、カウント、ハッシュコード (内部フィールド) = 12 バイト

1 オブジェクト ヘッダー = 8 バイト

典型的な Java 文字列の 1 = 20 + 4 + 8 + 8 + 12 + 8 = 60 バイト

単純なセルのサイズ (およそ、文字列を含む)

5 文字列 = 60 * 5 = 300 バイト

5 文字列参照 = 5 * 8 バイト = 40 バイト

1 オブジェクト ヘッダー = 8 バイト

1 SimpleCell = 300 + 40 + 8 = 308 バイト

160000 SimpleCell = 348 * 160000 = 55680000 バイト

明らかに、790 Mb (リークのように見えます) からはかなり離れていますが、推定値よりもほぼ 1 桁大きくなっています。

于 2012-09-19T19:54:29.880 に答える