Java引用
- 強引用(Strong Reference)
只要強引用存在,則不會回收.
static class Person {
byte[] content = new byte[1024 * 1024];// 1MB
}
static class Country {
byte[] content = new byte[1024 * 1024 * 10];// 10MB
}
static void testStrongReference() {
Person person = new Person();
System.out.println(person);
System.gc();
System.out.println(person);
}
我們可以看到,即使調用了GC對象仍然存在,輸出
com.citi.yf.referenceTest.Test$Person@15db9742
gc log 省略 ... ...
com.citi.yf.referenceTest.Test$Person@15db9742
- 軟引用(Soft Reference)
在系統將要發生OOM之前,把軟引用的對象進行回收,如果內存還不夠則OOM.
如果JVM內存足夠,手動調用System.gc() ,不會回收這部分對象.
static void testSoftReference() {
//create 10MB object
SoftReference<Country> ref = new SoftReference<Country>(new Country());
System.out.println(ref.get());
//create 25MB objects, 10+25>30, will trigger GC
java.util.List<Person> persons = new ArrayList<>();
for (int i = 0; i < 25; i++) {
persons.add(new Person());
}
System.out.println(ref.get());
}
run configurations:
-XX:+PrintGCDetails -Xms30m -Xmx30m
我們將jvm內存定在30MB, 測試中先創建了10MB的country, 在接下來的創建25*1MB的persons中,必定會出現GC, 看看是否會回收country.
com.citi.yf.referenceTest.Test$Country@15db9742
gc log 省略 ... ...
null
最后輸出的country為null,說明已被回收.
- 弱引用(Weak Reference)
GC的時候回收這部分對象.
static void testWeakReference() {
WeakReference<Person> ref = new WeakReference<Person>(new Person());
System.out.println(ref.get());
System.gc();
System.out.println(ref.get());
}
com.citi.yf.referenceTest.Test$Person@15db9742
gc log 省略 ... ...
null
- 虛引用(Phantom Reference)
一個對象是否有虛引用不影響其生存時間,且通過虛引用獲得的對象一直是null.
static void testPhantomReference(){
PhantomReference<Person> ref = new PhantomReference<Test.Person>(new Person(), null);
System.out.println(ref.get());
System.gc();
System.out.println(ref.get());
}
null
gc log 省略 ... ...
null
Java對象的回收
- 判斷對象是否需要回收
引用計數法
對象添加一個引用計數器,每當有一個地方引用他,計數器+1;引用失效則計數 器-1。計數器=0時就是需要回收之時。
無法回收互相引用的對象。-
可達性分析
以GC Roots為起始點開始向下搜索,搜索走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證明該對象是不可用的。可作為GC Roots的對象:
- 虛擬機棧中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中引用的對象
- 對象回收流程:
GC Root不可達對象->標記&篩選->二次標記, 如果對象被兩次標記,那么他基本上被真的回收了。 從上面的流程可以看出,對象可以在finalize方法中把自己救活,但是只能自救一次,因為finalize只能執行一次。
篩選:剔除沒有覆蓋 finalize()方法或者執行過finalize()方法的對象。
類的回收
同時滿足:
- 該類所有的實例已被回收
- 加載該類的classLoader已被回收
- 該類對應的java.lang.Class對象沒有在任何地方被引用