Java引用介绍与应用

注:本文作者是阿里巴巴B2B的谢中富;

一、前言

前阵子在工作中遇到java.lang.Reference这个类,发现很多三方库中都用到这个类,如apache中的httpclient工具等,所以就找了一些资料了解了下。

本文主要介绍Java中SoftReference、WeakReference、PhantomReference这三种引用类型的作用以及与GC的关系。之后引用ibm社区上的一个例子说明WeakReference可以与GC配合防止应用中一些内存泄漏的问题。

中间如有理解不正确的地方,欢迎大家指正。

二、引用对象介绍

首先介绍下两个比较容易混淆的概念,引用和引用对象。

引用是指对象的引用,在代码中一般都是通过引用来获取对象实例进行数据操作;引用对象是指JDK中java.lang.ref包下的一些类,具体下文中有介绍。

  1. 强引用

    在编码过程中最常见的就是这种用法如:
    Object obj = new Object();
    上面这段代码将在JVM堆中申请一块空间存储obj对象,只要obj引用存在,垃圾回收器就不会释放该空间。
    GC根据一个对象是否可以通过强引用链可达来判断是否可以回收其内存。

    在Java 1.2中引入了SoftReference、WeakReference、PhantomReference这三种引用对象,类结构关系如下图。



    Reference是这三个引用对象的抽象基类。方法摘要如下:



      当调用get()方法时可以返回一个强引用,如果返回null说明此引用已经被垃圾回收,所以也可以用这个方法来检测引用对象是否被垃圾回收。

    1. SoftReference软可及引用对象

      通常用来设计缓存,特点是被Soft Reference 引用的对象,要到JVM内存不足时且才会被GC回收,所以程序要允许此引用对象可空,不能强依赖于引用对象。此种类型的引用主要用于内存比较敏感的高速缓存。

    2. WeakReference弱可及引用对象

      此类型引用的生命周期要比SoftReference短。在每次GC时,一旦发现有WeakReference的引用,不管当前内存空间足够与否,都会回收此内存,即不会影响GC的策略。

    3. PhantomReference虚可及引用对象(也称幽灵引用对象)

      这个类只能用于跟踪被引用对象进行的收集。当GC确定了某个对象是虚可及对象时,此对象就被放在了它的ReferenceQueue上,表明引用对象已经结束,可以回收了。

    JAVA对象的生命周期
    再介绍下JAVA对象的生命周期,对上面的概念理解会有一定的帮助。在JVM里,对象整个生命周期分为以下七个阶段:


    三、利用WeakHashMap防止HashMap引起的内存泄漏

    最常见的内存泄漏是用Map存储临时对象,如以下例子:

    public class SocketManager {
    
    /*想要在sokcet连接的时候传递用户信息,并在连接已经建立的时候拿到这个用户信息,下面的处理方式看似合理,
    但是除非你确切知道socket什么时候被释放,并且同时把User引用从map中移除,否则map中的数据将永远不会被销毁,
    只要程序的运行时间列长,最终会报out of memory*/
    
    	private Map<Socket,User> m = new HashMap<Socket,User>();
    
    	public void setUser(Socket s, User u) {
     		m.put(s, u);
    	}
    	public User getUser(Socket s) {
    		return m.get(s);
    	}
    	public void removeUser(Socket s) {
     		m.remove(s);
     	}
    }
    
    SocketManager socketManager;
    ......
    socketManager.setUser(socket, user());

    上面内存泄漏的例子主要是没有一种机制使socket与user的生命周期保持一致。利用WeakReference,就可以声明对象在生命周期中的依赖关系。

    WeakHashMap的内部实现就是利用了WeakReference机制,value的引用依赖于key的生命周期,当key被释放时,value也将被释放。

    利用WeakHashMap修改后的代码:

    public class SocketManager {
    	private Map<Socket,User> m = new WeakHashMap<Socket,User>();
    	
    	public void setUser(Socket s, User u) {
    		m.put(s, u);
    	}
    	public User getUser(Socket s) {
    		return m.get(s);
    	}
    }

    四、引用的其它应用场景

    最后举一个ibatis cache的例子
    <cacheModel id="SysConstants-Cache" type="MEMORY" readOnly="true" serialize="false">
    	<flushInterval hours="2" />
    	<property name="reference-type" value="SOFT" />
    </cacheModel>

    上面cacheModel 中的reference-type属性可以设置以下三个值

    • WEAK,产生内存回收动作时,失效。
    • SOFT,内存不足时,失效。
    • STRONG,显式刷新时,失效。

    reference-type 配置的是cache的缓存策略,这三种缓存策略的原理其实就是运用了Java中对应的引用对象机制。

    五、参考资料

    http://www.artima.com/insidejvm/ed2/gcP.html
    http://www.ibm.com/developerworks/java/library/j-jtp11225/
    http://www.ibm.com/developerworks/java/library/j-jtp01246/index.html




posted @ 2013-04-18 16:41  java程序员填空  阅读(178)  评论(0编辑  收藏  举报