1. 程式人生 > 其它 >JVM四種引用級別

JVM四種引用級別

強引用

Object obj = new Object();

強引用物件什麼時候失效?

  1. 生命週期結束(作用域失效)

    public void method(){
        Object obj = new Object();
    }
    //當方法執行完畢之後,強引用指向的引用物件[new Object()]就會等待被GC回收
    
  2. 引用被置為null

    obj = null;
    

除了以上兩種情況以外,其他任何時候的GC都不會回收強引用物件。

軟引用

根據記憶體情況回收:如果記憶體充足,GC不會隨便回收軟引用物件。如果記憶體不足,GC就會主動回收軟引用物件。

package qx.leizige.ref;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

//軟引用物件
class Student {
    private String name;
}

/**
 * @author leizige
 * 2022/01/29
 */
public class SoftReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        //softRef --> softObject
        SoftReference<Student> softRef = new SoftReference<>(new Student());

        //開啟新的執行緒監聽是否有軟引用物件被回收
        new Thread(() -> {
            while (true) {
                if (Objects.isNull(softRef.get())) {
                    System.out.println("軟引用物件[student]已經被回收......");
                    System.exit(0);
                }
            }
        }, "softRef Listener").start();


        //不斷得往集合中存放資料,模擬記憶體不足
        List<byte[]> byteList = new ArrayList<>();
        while (true) {
            if (Objects.nonNull(softRef.get())) {
                byteList.add(new byte[1024 * 1024]);    //每次add 1M 資料
            }
        }
    }
}

弱引用

回收時機:只要GC執行,就會將弱引用物件進行回收。

package qx.leizige.ref;

import java.lang.ref.WeakReference;

/**
 * @author leizige
 * 2022/01/29
 */
public class WeakReferenceDemo {

    public static void main(String[] args) throws InterruptedException {


        WeakReference<Object> weakRef = new WeakReference<>(new Object());

        System.out.println(weakRef.get() == null ? "weakRef 已被回收" : "weakRef 沒被回收");

        System.gc();    //建議GC執行一次回收
        Thread.sleep(1000);

        System.out.println(weakRef.get() == null ? "weakRef 已被回收" : "weakRef 沒被回收");

    }
}

虛引用

是否使用虛引用,和引用物件本身沒有任何關係。無法通過虛引用來獲取物件本身,PhantomReferenceget方法總是返回 null。

虛引用一般不會單獨使用,而是和引用佇列(ReferenceQueue)一起使用。

當GC回收一個物件時,發現該物件還有一個虛引用,就會將該物件放進引用佇列中,之後(虛引用出隊之後)再去回收該物件。

GC --> 如果有虛引用 --> 虛引用入隊 --> 虛引用出隊 --> 回收物件。

package qx.leizige.ref;


import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

/**
 * @author leizige
 * 2022/01/29
 */
public class PhantomReferenceDemo {

    public static void main(String[] args) throws InterruptedException {

        Person person = new Person("張三");

        //開啟一個引用佇列
        ReferenceQueue<Person> refQueue = new ReferenceQueue<>();

        //虛引用必須和佇列一起使用
        PhantomReference<Person> phantomRef = new PhantomReference<>(person, refQueue);

        System.out.println("phantomRef = " + phantomRef.get());
        System.out.println("refQueue = " + refQueue.poll());


        person = null;
        System.gc();
        Thread.sleep(500);

        System.out.println("person = " + person);
        System.out.println("phantomRef = " + phantomRef.get());
        //回收之後,回收的物件到了引用佇列裡面
        System.out.println("refQueue = " + refQueue.poll());

    }
}

特殊情況:如果重寫了 Person 物件的 finalize 方法,那麼JVM會延遲入隊。可能在第二次,也可能在第三次GC時入隊。

package qx.leizige.ref;


import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("即將回收該物件.....");
    }
}

/**
 * @author leizige
 * 2022/01/29
 */
public class PhantomReferenceDemo {

    public static void main(String[] args) throws InterruptedException {

        Person person = new Person("張三");

        //開啟一個引用佇列
        ReferenceQueue<Person> refQueue = new ReferenceQueue<>();

        //虛引用必須和佇列一起使用
        PhantomReference<Person> phantomRef = new PhantomReference<>(person, refQueue);

        System.out.println("phantomRef = " + phantomRef.get());
        System.out.println("refQueue = " + refQueue.poll());


        person = null;
        System.gc();

        System.out.println("person = " + person);
        System.out.println("phantomRef = " + phantomRef.get());
        //回收之後,回收的物件到了引用佇列裡面
        System.out.println("refQueue = " + refQueue.poll());

        person = null;
        System.gc();

        System.out.println("person1 = " + person);
        System.out.println("phantomRef = " + phantomRef.get());
        //回收之後,回收的物件到了引用佇列裡面
        System.out.println("refQueue1 = " + refQueue.poll());


        person = null;
        System.gc();

        System.out.println("person = " + person);
        System.out.println("phantomRef = " + phantomRef.get());
        //回收之後,回收的物件到了引用佇列裡面
        System.out.println("refQueue2 = " + refQueue.poll());


        person = null;
        System.gc();

        System.out.println("person = " + person);
        System.out.println("phantomRef = " + phantomRef.get());
        //回收之後,回收的物件到了引用佇列裡面
        System.out.println("refQueue3 = " + refQueue.poll());

        person = null;
        System.gc();

        System.out.println("person = " + person);
        System.out.println("phantomRef = " + phantomRef.get());
        //回收之後,回收的物件到了引用佇列裡面
        System.out.println("refQueue4 = " + refQueue.poll());

    }
}
If you’re going to reuse code, you need to understand that code!