文章目錄
- 1.如何判斷對象可以回收
- 1.1.引用計數法
- 1.2.可達性分析算法
- 2.五種引用
- 6.代碼示例
- 6.1軟引用
- 6.2 弱引用
1.如何判斷對象可以回收
1.1.引用計數法
- 當一個對象被其他變量引用,該對象計數加一,當某個變量不在引用該對象,其計數減一
- 當一個對象引用沒有被其他變量引用時,即計數變為0時,該對象就可以被回收
缺點:循環引用時,兩個對象的計數都為1,導緻兩個對象都無法被釋放
1.2.可達性分析算法
- JVM中的垃圾回收器通過可達性分析來探索所有存活的對象
- 掃描堆中的對象,看能否沿着GC Root對象為起點的引用鍊找到該對象,如果找不到,則表示可以回收
-
可以作為GC Root的對象
– 虛拟機棧(棧幀中的本地變量表)中引用的對象。
– 方法區中類靜态屬性引用的對象
– 方法區中常量引用的對象
– 本地方法棧中JNI(即一般說的Native方法)引用的對象
– 所有被同步鎖(
關鍵字)持有的對象。synchronized
示例
/**
* 示範GC Roots
*/
public class Demo2_2 {
public static void main(String[] args) throws InterruptedException, IOException {
List<Object> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
System.out.println(1);
System.in.read();
list1 = null;
System.out.println(2);
System.in.read();
System.out.println("end...");
}
}
運作程式,分别生成垃圾回收前後的
dunp
檔案
jps // 檢視程序号
// format=b 生成檔案格式為二進制;live主動觸發垃圾回收,保留存活對象;file表示存放位置
jmap -dump:format=b,live,file=1.bin 程序号51125
list1置空前,生成dunmp檔案1.bin
List<Object> list1 = new ArrayList<>();
list1
是局部變量,存在于活動棧幀;
new ArrayList<>()
産生的對象才是存在于堆中的對象。即此處
new ArrayList<>()
對應的那個對象才能作為根對象。
list1置空後,生成dunmp檔案2.bin
因為在執行
jmap -dump:format=b,live,file=2.bin 51125
使用了live參數,主動調用了垃圾回收。由于
list1
被置空,
list
對象無人引用,是以被垃圾回收了。是以在根對象中找不到了。
2.五種引用
強引用
隻有所有 GC Roots對象都不通過【強引用】引用該對象,該對象才能被垃圾回收
強引用對象回收
軟引用
僅有【軟引用】引用該對象時,在垃圾回收後,記憶體仍不足時會再次出發垃圾回收,回收軟引用對象
可以配合【引用隊列】來釋放軟引用自身
軟引用對象回收
此時A2對象可能被回收。
- A2對象僅僅隻被軟引用對象引用
- 在執行GC時,記憶體空間不足了,才會被垃圾回收
- 回收後,軟引用對象本身可以通過進入引用隊列進行釋放
弱引用
僅有【弱引用】引用該對象時,在垃圾回收時,無論記憶體是否充足,都會回收弱引用對象
可以配合【引用隊列】來釋放弱引用自身
弱引用對象的回收
此時A3對象可能會被回收
- A3對象僅僅被弱引用對象引用
- 當執行GC時,無論記憶體是否不足,都會被垃圾回收
- 回收後,弱引用對象本身可以通過進入引用隊列進行釋放
虛引用
必須配合【引用隊列】使用,主要配合 ByteBuffer 使用,被引用對象回收時,會将【虛引用】入隊, 由 Reference Handler 線程調用虛引用相關方法釋放【直接記憶體】
如上圖,B對象不再引用ByteBuffer對象,ByteBuffer就會被回收。但是直接記憶體中的記憶體還未被回收。這時需要将虛引用對象Cleaner放入引用隊列中,然後調用它的clean方法來釋放直接記憶體
虛引用對象的回收
虛引用一般是對直接記憶體配置設定的應用。
- 當聲明
時,ByteBuffer
會配置設定一塊直接記憶體,并把直接記憶體的位址傳遞給虛引用對象Cleaner。ByteBuffer
- 當
不再被強引用時,被回收後,直接記憶體還沒有被釋放。這時會将虛引用放入虛引用的引用隊列,由ByteBuffer
線程監控,發現虛引用對象,調用虛引用相關方法Reference Handler
釋放直接記憶體。Unsafe.freeMemory
終結器引用
無需手動編碼,但其内部配合【引用隊列】使用,在垃圾回收時,【終結器引用】入隊(被引用對象暫時沒有被回收),再由 Finalizer 線程通過【終結器引用】找到被引用對象并調用它的 finalize 方法,第二次 GC 時才能回收被引用對象
如上圖,B對象不再引用A4對象。這時終結器對象就會被放入引用隊列中,引用隊列會根據它,找到它所引用的對象。然後調用被引用對象的finalize方法。調用以後,該對象就可以被垃圾回收了
終結器引用的回收
- 所有的類都繼承自Object類,裡面有一個終結方法finalize()方法,當對象重寫了finalize()方法,且沒有強引用引用它時,它就可以被當成垃圾進行垃圾回收。
- 當對象沒有被強引用時,會由jvm為該對象建立一個對應的終結器引用。當這個對象被垃圾回收時,會将終結器引用加入引用隊列,但是對象不會被垃圾回收。
- 再由一個優先級較低的Finalizer線程去監控引用隊列是否有終結器引用,如果有,就通過終結器引用找到A4對象,調用其finalize()方法,等調用之後,等下一次垃圾回收時,就可以被垃圾回收了。
- 工作效率低,第一次GC不會回收對象,先将終結器引用入隊,等到第二次垃圾回收才有可能被回收。
6.代碼示例
6.1軟引用
# 虛拟機參數
-Xmx20m -XX:+PrintGCDetails -verbose:gc
/**
* 示範軟引用
* -Xmx20m -XX:+PrintGCDetails -verbose:gc
*/
public class Demo2_3 {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) throws IOException {
/*List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new byte[_4MB]);
}
System.in.read();*/
soft();
}
public static void soft() {
// list --> SoftReference --> byte[]
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
System.out.println("循環結束:" + list.size());
for (SoftReference<byte[]> ref : list) {
System.out.println(ref.get());
}
}
}
/**
* 示範軟引用, 配合引用隊列
*/
public class Demo2_4 {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用隊列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 關聯了引用隊列, 當軟引用所關聯的 byte[]被回收時,軟引用自己會加入到 queue 中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 從隊列中擷取無用的 軟引用對象,并移除
Reference<? extends byte[]> poll = queue.poll();
while( poll != null) {
list.remove(poll);
poll = queue.poll();
}
System.out.println("===========================");
for (SoftReference<byte[]> reference : list) {
System.out.println(reference.get());
}
}
}
6.2 弱引用
-Xmx20m -XX:+PrintGCDetails -verbose:gc
/**
* 示範弱引用
* -Xmx20m -XX:+PrintGCDetails -verbose:gc
*/
public class Demo2_5 {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
// list --> WeakReference --> byte[]
List<WeakReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
list.add(ref);
for (WeakReference<byte[]> w : list) {
System.out.print(w.get()+" ");
}
System.out.println();
}
System.out.println("循環結束:" + list.size());
}
}