java中的四種引用(強,軟,弱,虛)

1、強引用java

概念:當對象不可達時,便可回收。面試

/**
 * 強引用。當強引用指針不存在時,對象將被回收
 * 也能夠理解爲 ROOT 引用消失時,對象將被回收
 */
public class StrongReference {

    /**
     * jvm在執行gc時 將回調該方法
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        System.out.println("gc doing now !");
    }

    public static void main(String[] args) {
        StrongReference strongReference = new StrongReference();
        
        // 將引用指針移除
        strongReference = null;
        
        // 手動調用gc
        System.gc();
    }
}

2、軟引用數組

概念: 當內存空間不足時,將被回收。jvm

/**
 * Xmx2oM  設置最大堆內存爲20M
 * 軟引用 會在內存空間不夠時,進行 gc 操做。 從而 回收 軟引用對象
 */
public class MySoftReference {

    public static void main(String[] args) {
        SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]);

        System.out.println("第一次gc前 : byte[]:" + softReference.get());
        System.gc();
        System.out.println("第一次執行gc後 : byte[]:" + softReference.get());


        byte[] bytes = new byte[1024*1024*12];
        System.out.println("內存不夠後 : byte[]:" + softReference.get());
    }
}

 

上述代碼的補充:源碼中,我將虛擬機參數 -Xmx 設置爲20M。 我前後  new 出來的  兩個byte 數組均超過10M。 目標對象均會存放在老年代中。(按照 新生代 : 老年代 = 1:2.) 大概內存分佈爲。 老年代 20 * 2/3 約爲13.3M。 年輕代  約爲 20 * 1/3 約爲 6.7M。其中 年輕代 eden:s0:s1 = 8:1:1 故 Eden 約爲 5.3 M 。So/S1 約爲0.67M。  以下圖所示,基本跟計算出來的結果吻合。至於  多出來的部分,我的理解,jvm對計算出的堆大小自我調優勒。ide

基於對堆內存的分析。能夠看出。 第一次  老年代  裝入  10 M的 對象。  第二次  想要  裝入 12M 對象。確定裝不下。 因爲是 軟引用。 jvm gc 時會將這部份內容先回收掉。 因此這裏沒有 出現OOM 的問題。spa

3、弱引用 (比較重要,面試常考題之一)操作系統

備註: ThreadLocal 裏使用勒 弱引用。會產生內存泄露的問題。詳見 ( http://www.javashuo.com/article/p-mivrrmpp-nx.html .net

概念:jvm無視該引用。該對象可被弱引用對象獲取到對象的實例。 當jvm執行gc時,將直接被回收。(提早條件:沒有強引用存在)。線程

示例一:3d

/**
 * 弱引用 虛擬機無視該引用。只要gc 必定會被回收(前提該對象沒有被強引用)
 */
public class Test {

    public static void main(String[] args) {

        Teacher teacher = new Teacher();
        WeakReference<Teacher> weakReference = new WeakReference<Teacher>(teacher);
        System.out.println(weakReference.get());
        // 若不將 teacher 置爲null,則還存在一個強引用。不會被gc回收
        //teacher = null;
        System.gc();
        System.out.println(weakReference.get());
    }

示例二:

/**
 * User 對象持有 Teacher 對象的強引用。
 * 當teacher 被置爲null 時。 teacher 的強應用 隨之消失。
 * 而 User 對 Teacher 的強引用還在。 故 user.getTeacher() 並非null
 * 因此 teacher 並不會被 gc  回收。
 *
 * 當 user 也被置爲null時。 user 對 teacher 的強引用也不存在勒。
 * 此時 teacher  將會被gc回收
 */
public class Test1 {

    public static void main(String[] args) {
        User user = new User();
        Teacher teacher = new Teacher();
        teacher.setName("zs");
        user.setTeacher(teacher);

        WeakReference<Teacher> weakReference = new WeakReference<Teacher>(teacher);

        System.out.println(System.identityHashCode(weakReference.get()));
        System.out.println(System.identityHashCode(teacher));
        System.out.println(System.identityHashCode(user.getTeacher()));

        teacher = null;
        System.out.println(user.getTeacher().getName());

        // 當user 置爲null 時, 對teacher 的強引用消失。 此時 teacher 將會被回收。
        user = null;

        System.gc();
        System.out.println(weakReference.get());
    }
}

示例三:

public class People extends WeakReference<Teacher> {

    public People(Teacher referent) {
        super(referent);

    }
}

/**
 * People 繼承自WeakReference<T>  People也是一個虛擬引用對象。
 * 因此teacher 被置爲null時,強引用指針被清除。
 * teacher 就會被gc回收。
 */
public class Test2 {

    public static void main(String[] args) {

        Teacher teacher = new Teacher();
        People people = new People(teacher);

        teacher = null;

        System.gc();
        System.out.println(people.get());
    }
}

4、虛引用

概念:該引用很雞肋。一個對象被虛引用所引用時。並不能獲取到該實例的對象。只有當該對象被回收時,纔會收到經過並存入隊列中。基本上用於操做直接內存來使用的。 可用於對一些重要對象的gc的監聽。或者監聽gc的頻率(不如打印gc日誌來的簡單直觀)。

如下 jvm參數  -Xmx20M。 固然也能夠更小,主要是爲了看gc 的效果。

/**
 * 虛擬引用
 * 比較雞肋,虛擬引用的對象,並不能get()出來。 而是直接操做 操做系統內存的。
 * 虛擬引用的目標對象,被回收後,會存入隊列中
 * 須要新啓一個線程監聽該隊列中,是否有數據。有數據 則 回收掉直接內存。
 */
public class MyPhantomReference {

    private static  final List<Object> LIST = new LinkedList<Object>();
    private static  final ReferenceQueue<MyPhantomReference> QUEUE = new ReferenceQueue<MyPhantomReference>();

    public static void main(String[] args) {
        PhantomReference<MyPhantomReference>  phantomReference = new PhantomReference<MyPhantomReference>(new MyPhantomReference(),QUEUE);

        new Thread( () -> {
            while(true){
                LIST.add(new byte[1024*1024]);
                try{
                    Thread.sleep(1000);
                }catch(Exception e){
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(phantomReference.get());
            }
        }).start();


        new Thread( () -> {
            while(true){
                Reference<? extends MyPhantomReference> poll = QUEUE.poll();
                if(poll != null){
                  System.out.println("虛擬引用被jvm回收啦 ----" + poll);
                }
            }
        }).start();


        try{
           Thread.sleep(500);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

相關文章
相關標籤/搜索