天天看点

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
           
软引用的元素已经被回收了,但是引用还存在