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
軟引用的元素已經被回收了,但是引用還存在

發佈了48 篇原創文章 · 獲贊 104 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章