被static修飾的map、list GC問題

環境:java1.8   -Xms10m -Xmx10m -XX:+PrintGCDetails

最近在研究GC時,發生一些問題想了好久纔想明白,先上測試代碼。

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

class OOMData {

    private static ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();

    private static ArrayList<String> list = new ArrayList<>();

    public void testString() {
        String a = "www.baidu.com";
        while (true) {
            a += (new Random().nextInt(888888888) + new Random().nextInt(999999999)
                    + getRandomString(123) + getRandomString(345)
                    + getRandomString(789) + "哈哈哈哈哈啊哈哈哈哈"
                    + getRandomString(234) );
        }
    }

    public void testStaticMap() {
        int i = 1;
        String a = "test";
        while (true) {
            map.put(i++, getRandomString(1000));
        }
    }

    public void testStaticList() {
        while (true) {
            list.add("test");
        }
    }


    //length用戶要求產生字符串的長度
    public static String getRandomString(int length){
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }


}



public class OOMTest {

    public static void main(String[] args) {
        OOMData data = new OOMData();
        data.testStaticList();
    }

}

下面上測試結果:

運行testString()方法拋出的異常爲:(即測試字符串方法,jdk1.8以後將字符串常量池轉移到堆中,每次字符串改變後都會在堆中新建一個字符串對象,直至內存溢出,這個GC日誌分析請移步https://blog.csdn.net/weixin_38342534/article/details/102182145

java.lang.OutOfMemoryError: Java heap space


運行testStaticMap()方法後拋出的異常爲:

java.lang.OutOfMemoryError: GC overhead limit exceeded

運行testStaticList()方法拋出的異常爲:

java.lang.OutOfMemoryError: Java heap space

下面這兩個我剛開始不是很理解,因爲我都使用了static進行修飾,被static修飾的類會存放在元空間,不會佔用堆內存,而元空間的大小理論上跟筆記本的物理內存一樣大。且報錯並不是java.lang.OutOfMemoryError: Metaspace 一個是java.lang.OutOfMemoryError: GC overhead limit exceeded 即程序用98%的時間回收了不到2%的堆內存。一個是java.lang.OutOfMemoryError: Java heap space即堆old區滿了。貼出GC日誌

testStaticList

testStaticMap

 

通過GC日誌可以看出兩個都是因爲堆內存被佔滿導致的OOM,說下原因是因爲ArrayList底層是使用的數組,而這個數組是ArrayList定義的成員變量Object[],成員變量是存在堆內存中的,所以纔會java heap space。而ConcurrentHashMap底層的鏈表和紅黑樹結構也都是在類中定義的成員變量,而每次擴展也都會生成一些新的變量,(底層原理具體請看源碼或者別的博客)所以最後young區和old區全部被佔滿。

這說明,被static修飾的容器容易導致oom不是因爲存入了元空間,元空間內存不夠導致的,而是因爲被static修飾的容器,是GC Roots的對象之一,在它下面進行可達性分析的時候,會一直可達,所以會導致GC的時候,不能被回收,從而導致OOM

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章