天天看點

WeakHashMap回收時機結合JVM 虛拟機GC的一些了解

一直很想知道WeakHashMap的使用場景,想來想去隻能用在高速緩存中,而且緩存的資料還不是特别重要,因為key(key不存在被引用的時候)随時會被回收

是以研究了一下WeakHashMap的回收時機

呵呵,現在可以重視 String str = "abc" 跟 String Str = new String("abc") 的差別了,因為涉及到收回問題

String str = "abc" //這屬于編譯時生成的字面量,會放入運作時常量池,這個區域的收回條件非常苛刻,是以一般不會被回收,是以哪怕不存在引用,WeakHashMap的這個key也不容易被回收

String Str = new String("abc") //會放入堆記憶體,GC着重處理這個區

/**
 * 
 * @author ZhenWeiLai
 *
 */
public class TestWeakHashMap {
    
    static WeakHashMap<String,String> map = new WeakHashMap<>();
    //會被回收因為 map 的 key 用 new String 執行個體化了一個對象 儲存在堆裡,雖然是線程共享,但是并沒有任何引用指向這個key
        /**
         * 這裡補充一下,Java heap 是被所有線程共享的一塊記憶體區域
         * 幾乎所有的對象執行個體都在這裡配置設定記憶體,這裡說幾乎,是因為随着JIT編譯器的發展與逃逸分析技術逐漸成熟
         * 棧上配置設定,标量替換等優化技術将導緻一些微妙的變化發生,所有的對象都配置設定在堆上也漸漸變得不是那麼絕對
         */
    static {
        map.put(new String("a"),new String("abc"));
        map.put(new String("b"),new String("abc"));
        map.put(new String("c"),new String("abc"));
        map.put(new String("d"),new String("abc"));
        map.put(new String("e"),new String("abc"));
        map.put(new String("f"),new String("abc"));
        map.put(new String("g"),new String("abc"));
    }
    
    //會被回收
    static WeakHashMap<String,String> map4 = new WeakHashMap<>();
    static {
        map4.put(new String("a"),"abc");
        map4.put(new String("b"),"abc");
        map4.put(new String("c"),"abc");
        map4.put(new String("d"),"abc");
        map4.put(new String("e"),"abc");
        map4.put(new String("f"),"abc");
        map4.put(new String("g"),"abc");
    }
    
    
    static WeakHashMap<String,String> map2 = new WeakHashMap<>();
    //不會被收回,因為存在 方法區(以前也叫永久代,JAVA8已經不存在永久代) - 常量池
    /**
     * (Method Area 别名 Non-Heap) 與Java Heap 一樣,是各個線程共享的記憶體區域,
     * 以前這個區域也叫作 永久代,因為幾乎不會被回收
     * 它用于存儲已被虛拟機加載的類資訊,常量,靜态變量.即時編譯後的代碼等資料
     */
    /**
     * map2的key 是存在 運作時常量池,運作時常量池是 Method Area的一部分
     * Java并不要求常量一定隻有在編譯期才能産生,運作期間也可能将新的常量放入池中,具有代表性的就是String的intern()方法
     */
    static {
        map2.put("a","abc");
        map2.put("b","abc");
        map2.put("c","abc");
        map2.put("d","abc");
        map2.put("e","abc");
        map2.put("f","abc");
        map2.put("g","abc");
    }
    
    public static void main(String[] args) throws InterruptedException {
        while(true){
            /**
             * 解開注釋,map,map4的key将不會被回收
             * 我了解為,在棧(也叫線程私有棧,或者工作記憶體)中,每個線程會将共享資料拷貝到棧頂進行運算,
             * 這份資料其實是一個副本.(如果棧内部所包含的"局部變量"是引用,則僅僅是引用值在棧中,而且會占用一個引用本身的大小,具體的對象還是在堆當中,即對象本身的大小與棧空間的使用無關)
             * 是以這個map存在一個引用,就不會去回收它的key
             */
//            System.out.println("map:"+map.size());
//            System.out.println("map2:"+map2.size());
//            System.out.println("map4:"+map4.size());
            
            //模拟被一個線程調用,然後休眠5秒,會随機被回收
            new Thread(()->{
                System.out.println("map:"+map.size());
                System.out.println("map2:"+map2.size());
                System.out.println("map4:"+map4.size());
                System.out.println("-------------------");
            }).start();
            TimeUnit.SECONDS.sleep(5);
        }
    }
}