java 物件記憶體膨脹與Set記憶體占用問題研究

2021-08-27 16:42:12 字數 1213 閱讀 8123

遇到乙個有意思的業務。

有兩個單列檔案,乙個500m乙個700m,共1.2g,2e個資料,要將這兩個單列檔案中的資料提取出來去重。

最簡單的思路,理論大小為1.2g的資料塞進set裡直接去重,發現程式跑著跑著就跑不動了,用jstat檢視,發現原來沒有賦予初始化引數,預設的初始化堆記憶體太小,導致程式跑不動。

[b]於是在啟動的時候加上了-xms3000m -xmx3000m[/b]

再次啟動,用jstat觀察發現老年代占用一直在**,一段時間之後老年代被佔滿,full gc也gc不掉老年代的物件,於是程式再次卡住不動,檢查程式沒有記憶體洩露/死迴圈之後,分析,理論大小1.2g的資料,為何會在記憶體中占用超過3g的堆記憶體。

首先檢查了set的內部實現,jdk中set的實現是用hashmap實現的,而hashmap底層則是兩個陣列,這裡關注到了map中陣列的自動擴容演算法:當不停的向map中put資料的過程中,如果資料量超過了map中設定的閥值threshold,那麼map就會自動擴容,將原來陣列中的資料複製到乙個2倍於原來容量的新陣列中。

void resize(int newcapacity)

entry newtable = new entry[newcapacity];

transfer(newtable);

table = newtable;

threshold = (int)(newcapacity * loadfactor);

}

這裡可以清楚的看到,map在移動資料的過程中,需要新建乙個陣列,而當newcapacity大到一定數量級的時候,就會占用非常多的記憶體。通過跟蹤檢查,發現3g內存在陣列擴容到5000w大小的時候就抗不住了,於是單獨做了乙個實驗,單獨的構造乙個5000w大小的陣列,觀察其記憶體占用情況,發現確實需要非常多的記憶體。此為原因之一。

然後,又觀察到,雖然構建大陣列,需要很多的記憶體空間,但是這部分空間並沒有大到記憶體無法承受。跟蹤檢查到,老年代被佔滿的時候,構造的set裡容納了2700w個物件,粗略的估算3g被2700w個物件所佔據,每個物件大約需要100個位元組的大小(由於無法精確的減除掉陣列和其他因素占用的記憶體,所以實際值肯定比100位元組要小),但是檔案資料為16位的字串,換句話說,每個16位的字串物件,在記憶體裡佔據了大約100個位元組的空間,這比檔案編碼後的字元大小要大很多。結論:轉化為物件的時候,資料會膨脹,並且體積膨脹的大小比我們想象的要更大得多。

最後,丟到hive裡輕輕鬆鬆解決了問題。

java物件記憶體

堆區 1.儲存的全部是物件,每個物件都包含乙個與之對應的class的資訊。class的目的是得到操作指令 2.jvm只有乙個堆區 heap 被所有執行緒共享,堆中不存放基本型別和物件引用,只存放物件本身。棧區 1.每個執行緒包含乙個棧區,棧中只儲存基礎資料型別的物件和自定義物件的引用 不是物件 物件...

set方法與記憶體管理

區域性變數,使用完以後不再使用就release 全域性變數,在dealloc裡面release。指標物件要自己操作自己。retaincount指的是物件引用計數。self賦值方式 computer cmp computer alloc init cmp物件retaincount 1 self.com...

java物件占用記憶體分析

前言 空物件佔8個位元組 有資料成員的話,你把資料成員按基本資料型別和物件引用分開統計。基本資料型別按byte boolean 1,char short 2,int float 4,long double 8,累加,然後對齊到8的倍數。物件引用按每個4位元組,累加,然後對齊到8個位元組的倍數。物件占...