天天看點

jvm垃圾收集器回收什麼樣的對象以及各種引用

jvm可大緻分為5部分,其中程式計數器、虛拟機棧、本地方法棧,是線程私有的,随線程分方法的調用生滅。GC回收的對象主要集中在堆區和方法區。

引用計數法:

每當一個地方引用該對象,該對象的引用計數器加1,引用失效後引用計數器減1。

算法簡單,快速,但是無法解決循環引用的問題,維護引用計數器增加消耗。

目前jvm沒有采用該算法。

可達性分析:

通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鍊(Reference Chain),當一個對象到GC Roots沒有任何引用鍊相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。

常用來做GCROOTS的有全局性的引用和執行上下文:(1)虛拟機棧中的中本地變量表引用的對象。(對象存活周期是棧幀的入棧到出棧,出棧後棧中引用的對象生命周期截至,GcRoots斷開,與該GcRoots相連的對象可以被回收)

(2)方法區中類靜态屬性引用的對象

(3)方法區中常量引用的對象

(4)本地方法棧中JNI引用的對象

應用的分類:

強引用,引用還在不回收。

軟引用(SoftReference):記憶體不夠時回收。軟引用可用來實作記憶體敏感的高速緩存

弱引用(WeakReference):下次垃圾回收時回收。

虛引用:不能通過虛引用找到對象,隻是作為垃圾回收時擷取通知的方式。

單條引用路徑可及性判斷:在這條路徑中,最弱的一個引用決定對象的可及性。

多條引用路徑可及性判斷:幾條路徑中,最強的一條的引用決定對象的可及性。

SoftReference和WeakReference均為對象,将某個對象作為參數傳入其中,當該對象要回收的時候,隻是回收了該對象,并沒有回收軟引用和弱引用的對象,是以要在軟引用和弱引用聲明時要綁定一個引用隊列,在軟引用和弱引用鎖引用的對象會收時幫助GC回收軟引用和弱引用的對象。

下面是針對各種引用的介紹這篇文章中的一個用軟引用實作的緩存程式稍加改動,說明一下上面提到的問題。

package com;
public class Employee {
    private String id;// 雇員的辨別号碼
    private String name;// 雇員姓名
    private String department;// 該雇員所在部門
    private String Phone;// 該雇員聯系電話
    private int salary;// 該雇員薪資
    private String origin;// 該雇員資訊的來源
    private byte[][] photo = new byte[100][1024*1024];// 增加了一個自己數組,友善出發GC
    // 構造方法
    public Employee(String id) {
       this.id = id;
       getDataFromlnfoCenter();
    }
    String getID(){
    	return id;
    }
    // 到資料庫中取得雇員資訊
    private void getDataFromlnfoCenter() {
       // 和資料庫建立連接配接井查詢該雇員的資訊,将查詢結果指派
       // 給name,department,plone,salary等變量
       // 同時将origin指派為"From DataBase"
    }
}
           
<pre name="code" class="java">package com;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;
public class EmployeeCache {
    static private EmployeeCache cache;// 一個Cache執行個體
    private Hashtable<String,EmployeeRef> employeeRefs;// 用于Chche内容的存儲
    private ReferenceQueue<Employee> q;// 垃圾Reference的隊列
 
    // 繼承SoftReference,使得每一個執行個體都具有可識别的辨別。
    // 并且該辨別與其在HashMap内的key相同。
    private class EmployeeRef extends SoftReference<Employee> {
       private String _key = "";
 
       public EmployeeRef(Employee em, ReferenceQueue<Employee> q) {
           super(em,q);
           //super(em);//用來測試不加引用隊列消除軟引用的情況
           _key = em.getID();
       }
    }
 
    // 建構一個緩存器執行個體
    private EmployeeCache() {
       employeeRefs = new Hashtable<String,EmployeeRef>();
       q = new ReferenceQueue<Employee>();
    }
 
    // 取得緩存器執行個體
    public static EmployeeCache getInstance() {
       if (cache == null) {
           cache = new EmployeeCache();
       }
       return cache;
    }
 
    // 以軟引用的方式對一個Employee對象的執行個體進行引用并儲存該引用
    private void cacheEmployee(Employee em) {
       cleanCache();// 清除垃圾引用
       EmployeeRef ref = new EmployeeRef(em, q);
       employeeRefs.put(em.getID(), ref);
    }
 
    // 依據所指定的ID号,重新擷取相應Employee對象的執行個體
    public Employee getEmployee(String ID) {
       Employee em = null;
       // 緩存中是否有該Employee執行個體的軟引用,如果有,從軟引用中取得。
       if (employeeRefs.containsKey(ID)) {
           EmployeeRef ref = (EmployeeRef) employeeRefs.get(ID);
           em = (Employee) ref.get();
       }
       // 如果沒有軟引用,或者從軟引用中得到的執行個體是null,重新建構一個執行個體,
       // 并儲存對這個建立執行個體的軟引用
       if (em == null) {
           em = new Employee(ID);
           System.out.println("Retrieve From EmployeeInfoCenter. ID=" + ID);
           this.cacheEmployee(em);
       }
       return em;
    }
 
    // 清除那些所軟引用的Employee對象已經被回收的EmployeeRef對象
    private void cleanCache() {
       EmployeeRef ref = null;
       while ((ref = (EmployeeRef) q.poll()) != null) {

    	   System.out.println("回收了元素:"+ref._key);
           employeeRefs.remove(ref._key);
       }
    }
 
    // 清除Cache内的全部内容
    public void clearCache() {
       cleanCache();
       employeeRefs.clear();
       System.gc();
       System.runFinalization();
    }
    public int size() {
		return employeeRefs.size();
	}
    public Employee getEmployeeAfterGC(String ID) {//增加了一個函數,提供在GC後檢視元素是否在存在
        Employee em = null;
        // 緩存中是否有該Employee執行個體的軟引用,如果有,從軟引用中取得。
        if (employeeRefs.containsKey(ID)) {
            EmployeeRef ref = (EmployeeRef) employeeRefs.get(ID);
            em = (Employee) ref.get();
        }
        
        return em;
     }
    public EmployeeRef getEmployeeRefAfterGC(String ID) {//檢視GC之後,軟引用的回收情況
        return employeeRefs.get(ID);
     }
}
           
主函數
           
<pre name="code" class="java">public class Demo {

	int i = 0;

	public static void main(String[]args)
	{
		
		EmployeeCache cache = EmployeeCache.getInstance();
		for (int i = 0; i < 10; i++) {			
			cache.getEmployee(String.valueOf(i));
		}
		for (int i = 0; i < 10; i++) {
			System.out.println("第"+i+"元素的對象回收:"+(cache.getEmployeeAfterGC(String.valueOf(i))==null));
			System.out.println("第"+i+"元素的軟引用回收:"+(cache.getEmployeeRefAfterGC(String.valueOf(i))==null));
		}
		
	}

}
           
在加入引用隊列去除map中的無效引用時,運作結果
           
回收了元素:8
回收了元素:7
回收了元素:6
回收了元素:5
回收了元素:4
回收了元素:3
回收了元素:2
回收了元素:1
回收了元素:0
第0元素的對象回收:true
第0元素的軟引用回收:true
第1元素的對象回收:true
第1元素的軟引用回收:true
第2元素的對象回收:true
第2元素的軟引用回收:true
第3元素的對象回收:true
第3元素的軟引用回收:true
第4元素的對象回收:true
第4元素的軟引用回收:true
第5元素的對象回收:true
第5元素的軟引用回收:true
第6元素的對象回收:true
第6元素的軟引用回收:true
第7元素的對象回收:true
第7元素的軟引用回收:true
第8元素的對象回收:true
第8元素的軟引用回收:true
第9元素的對象回收:false
第9元素的軟引用回收:false
           
去掉引用隊列之後:
           
第0元素的對象回收:true
第0元素的軟引用回收:false
第1元素的對象回收:true
第1元素的軟引用回收:false
第2元素的對象回收:true
第2元素的軟引用回收:false
第3元素的對象回收:true
第3元素的軟引用回收:false
第4元素的對象回收:true
第4元素的軟引用回收:false
第5元素的對象回收:true
第5元素的軟引用回收:false
第6元素的對象回收:true
第6元素的軟引用回收:false
第7元素的對象回收:true
第7元素的軟引用回收:false
第8元素的對象回收:true
第8元素的軟引用回收:false
第9元素的對象回收:false
第9元素的軟引用回收:false
           
軟引用的元素已經被回收了,但是引用還存在