SoftReference軟引用

示例代碼:

import java.lang.ref.SoftReference;

/**
 * @author chenjc
 * @since 2020-01-13
 */
public class SoftReferenceTest {

    /**
     * 使用JVM參數-Xmx10m運行程序
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        User user = new User(1, "debo");
        // 建立User對象的軟引用
        SoftReference<User> userSoftReference = new SoftReference<>(user);
        // 去掉強引用
        user = null;
        System.out.println(userSoftReference.get());
        // 手動觸發GC
        System.gc();
        System.out.println("第一次GC: " + userSoftReference.get());
        // 分配適量內存空間,造成內存資源緊張,產生GC,同時又不會導致堆內存溢出
        byte[] bytes = new byte[6 * 1024 * 1050];
        System.out.println("第二次GC: " + userSoftReference.get());
    }

    private static class User {
        private Integer id;
        private String name;

        public User(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

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

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

使用JVM參數-Xmx10m運行此程序,輸出如下:

User{id=1, name='debo'}
第一次GC: User{id=1, name='debo'}
第二次GC: null

第一次GC的時候,軟引用沒有被回收,是因爲這時候內存資源充足。第二次由於分配了較大的內存,導致GC,這時候由於內存資源緊張,軟引用被回收了,也就是雖然User對象有一個軟引用在引用着它,但User對象在此條件下也會被GC回收。所以軟引用的對象在一定條件下可被回收,故軟引用對象不會導致內存溢出。

軟引用到底有沒有被回收,可以通過給軟引用一個ReferenceQueue來跟蹤,將上面的代碼片段稍作修改,如下:

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

/**
 * @author chenjc
 * @since 2020-01-13
 */
public class SoftReferenceTest {

    /**
     * 使用JVM參數-Xmx10m運行程序
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        User user = new User(1, "debo");
        // 建立User對象的軟引用
        ReferenceQueue<User> userReferenceQueue = new ReferenceQueue<>();
        UserSoftReference userSoftReference = new UserSoftReference(user, userReferenceQueue);
        // 去掉強引用
        user = null;
        System.out.println(userSoftReference.get());
        // 手動觸發GC
        System.gc();
        System.out.println("第一次GC: " + userSoftReference.get());
        System.out.println("第一次GC隊列: " + userReferenceQueue.remove(1000));
        // 分配適量內存空間,造成內存資源緊張,產生GC,同時又不會導致堆內存溢出
        byte[] bytes = new byte[6 * 1024 * 1055];
        System.out.println("第二次GC: " + userSoftReference.get());
        Reference<? extends User> reference = userReferenceQueue.remove(1000);
        if (reference != null) {
            UserSoftReference userSoftReference1 = (UserSoftReference) reference;
            System.out.println("第二次GC隊列: " + userSoftReference1.getId());
        }
    }

    private static class User {
        private Integer id;
        private String name;

        public User(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

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

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

    private static class UserSoftReference extends SoftReference<User> {

        private Integer id;

        public UserSoftReference(User referent, ReferenceQueue<? super User> q) {
            super(referent, q);
            this.id = referent.id;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }
    }
}

輸出如下:

User{id=1, name='debo'}
第一次GC: User{id=1, name='debo'}
第一次GC隊列: null
第二次GC: null
第二次GC隊列: 1

第一次GC沒有回收軟引用對象,所以ReferenceQueue爲空,第二次GC回收了軟引用對象,所以ReferenceQueue隊列不爲空,那爲什麼可以強轉成UserSoftReference呢?是因爲隊列裏面的reference就是方法局部變量userSoftReference。此處自定義一個UserSoftReference類主要是爲了跟蹤User對象的id,你無法跟蹤User對象,因爲User對象已經被回收了,如果調用reference.get(),將會返回null。

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