JVM四種引用級別
阿新 • • 發佈:2022-01-29
強引用
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!