JAvaオブジェクトメモリ膨張とSetメモリ占有問題の研究


面白い業務に出会う.
2つの単列ファイル、1つの500 M 1つの700 M、合計1.2 G、2 Eのデータがあり、この2つの単列ファイルのデータを抽出して重量を除去します.
最も簡単な構想、理論の大きさの1.2 GのデータはSetの中に詰め込んで直接重さに行って、プログラムが走って走っていることを発見して走って動かないことを発見して、jstatで調べて、もとは初期化のパラメータを与えていないことを発見して、デフォルトの初期化のスタックのメモリは小さすぎて、プログラムが走れないことを招きます.
そこで起動時に-Xms 3000 m-Xmx 3000 mを追加しました
再度起動して、jstatで観察して古い年代の占用がずっと上昇していることを発見して、しばらくの時間の後に古い年代はいっぱい占められて、Full GCも古い年代の対象を落とすことができなくて、そこでプログラムは再び動かなくて、プログラムがメモリが漏れていないことを検査します/死んで循環した後に、分析して、理論の大きさの1.2 Gのデータ、どうしてメモリの中で3 Gを超えるスタックのメモリを占用します.
まず,Setの内部実装を検討したが,JDKにおけるSetの実装はHashMapで実現され,HashMapの下層は2つの配列であり,ここではMapにおける配列の自動拡張アルゴリズムに注目した:Mapにputデータを絶えず入力する過程で,データ量がMapに設定されたバルブ値thresholdを超えると,Mapは自動拡張され,元の配列のデータを元の容量の2倍の新しい配列にコピーします.

void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

ここでは、Mapがデータを移動する過程で、新しい配列を作成する必要があり、newCapacityが一定のレベルに大きくなると、非常に多くのメモリが消費されることが明らかになりました.追跡検査により、3 Gメモリは配列が5000 wサイズに拡張されたときに耐えられないことが分かった.そこで、単独で実験を行い、5000 wサイズの配列を単独で構築し、メモリの占有状況を観察したところ、確かに非常に多くのメモリが必要であることが分かった.これが原因の1つです.
そして,大きな配列を構築するには多くのメモリ空間が必要であるが,この部分はメモリが耐えられないほど大きくないことが観測された.追跡検査では,旧世代が満タンになったとき,構造のSetには2700 w個のオブジェクトが収容され,大まかな推定3 Gは2700 w個のオブジェクトが占有され,オブジェクトごとに約100バイトのサイズが必要です(配列やその他の要因で消費されるメモリを正確に除去できないため、実際の値は100バイトよりも小さいに違いない)が、ファイルデータは16ビットの文字列である.言い換えれば、16ビットの文字列オブジェクトごとに、メモリに約100バイトの空間が占められており、ファイル符号化後の文字サイズよりもはるかに大きい.結論:オブジェクトに変換する際、データ膨張し、体積膨張の大きさは私たちが想像していたよりずっと大きいです.
最後に、hiveに捨てて軽く問題を解決しました.