Loading

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());

    }
}
posted @ 2022-01-29 16:34  不颓废青年  阅读(68)  评论(0编辑  收藏  举报