同事有段代碼執行時間過長,需要進行優化,
Hashmultimap<Int,Bean> map = ...; for (400w*96) { // 計算過程 Bean = doCompute(); // 緩存計算結果 map.put(int,Bean); }
剛開始以為是計算過程doCompute效率低造成的,所以想各種方法優化計算,提前計算、多線程、。。。。等等等等,最終如下
// 多線程計算 .... // 計算結果放入隊列 ConcurrentLinkedQueue queue = ...; queue.offer(doCompute()); ..... // 隊列數據放入緩存 Hashmultimap<Int,Bean> map = ...; map.put(int,queue.poll());
但還是不行,跑跑就cpu 100%,找不到什麼原因,直到快下班時突然想到是不是內存引起的,然後放到服務器上一執行,-Xms20G -Xmx20G,沒有任何問題。
分析了下,找到了原因(純分析,下周打印下jvm日志驗證下):
對於Hashmultimap和ConcurrentLinkedQueue來說,因為是循環單個放入緩存,所以jvm是逐漸到達最大堆棧的,而一旦jvm發現內存不夠用,就啟動GC過程,但GC結束發現還是不夠用(因為沒有實例需要回收)就又啟動GC,。。。,循環往復,業務代碼不能跑,CPU全被GC占滿,而且也不拋內存溢出的異常。
問題定位過程曲折:先入為主容易鑽死胡同、好為人師的人真nm多、要相信jvm的計算生產水平。。。。
另外,記錄下一些可能用到的知識:
JVM初始分配的內存默認是物理內存的1/64,默認(MinHeapFreeRatio參數可以調整)空余堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制,-XX:MinHeapFreeRatio=40.
JVM最大分配的內存默認是物理內存的1/4,默認(MaxHeapFreeRatio參數可以調整)空余堆內存大於70%時,JVM會減少堆直到 -Xms的最小限制,-XX:MaxHeapFreeRatio=70.
通過free查看到的空閒內存不能完全分配到jvm,能分配的比例應該和jvm廠商、os有關。