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!