JVM四种引用级别
强引用
Object obj = new Object();
强引用对象什么时候失效?
- 
生命周期结束(作用域失效) public void method(){ Object obj = new Object(); } //当方法执行完毕之后,强引用指向的引用对象[new Object()]就会等待被GC回收
- 
引用被置为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 没被回收");
    }
}
虚引用
是否使用虚引用,和引用对象本身没有任何关系。无法通过虚引用来获取对象本身,PhantomReference的 get方法总是返回 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!

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号