Java的四种引用之强弱软虚

转摘地址:http://www.cnblogs.com/zwbg/p/6196000.htmlhtml

在java中提供4个级别的引用:强引用、软引用、弱引用和虚引用。除了强引用外,其余3中引用都可以在java.lang.ref包中找到对应的类。开发人员能够在应用程序中直接使用他们。java

1 强引用缓存

强引用就是程序中通常使用的引用类型,强引用的对象是可触及的,不会被回收。相对的,软引用、弱引用和虚引用的对象是软可触及的、弱可触及的和虚可触及的,在必定条件下,都是能够被回收的。jvm

强引用示例:StringBuffer str = new StringBuffer("Hello world");ide

假设以上代码是在函数体内运行的,那么局部变量str将被分配在栈上,而对象StringBuffer实例被分配在堆上。局部变量str指向StringBuffer实例所在堆空间,经过str能够操做该实例,那么str就是StringBuffer实例的强引用。函数

此时,若是再运行一个赋值语句:this

StringBuffer str1 = str;.net

则str所指向的对象也将被str1所指向,同时在局部变量表上会分配空间存放str1变量。此时该StringBuffer实例就有两个引用。对引用的“==”操做用于表示两操做数所指向的堆空间地址是否相同,不表示两操做数所指的对象是否相同。线程

强引用的特色:htm

强引用能够直接访问目标对象 强引用所指向的对象在任什么时候候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指的对象。 强引用可能致使内存泄漏

2 软引用----可被回收的引用

软引用是比强引用弱一点的引用类型,一个对象只持有软引用,那么当堆空间不足时,就会被回收。软引用使用java.lang.ref.SoftReference类型

package com.jvm; import java.lang.ref.SoftReference; public class SoftRef {   public static class User{     public User(int id,String name){       this.id = id;       this.name = name;     }     public int id;     public String name;     @Override     public String toString() {       return "[id="+String.valueOf(id)+",name="+name+"]";     }   }   public static void main(String[] args) {     User u = new User(1,"gim");     SoftReference<User> userSoftRef = new SoftReference<User>(u);//建立引用给定对象的新的软引用     u = null; //去除强引用

    System.out.println(userSoftRef.get());     System.gc();     System.out.println("After GC:");     System.out.println(userSoftRef.get());

    byte[] b = new byte[10249257];//分配7M左右,使内存空间紧张     System.gc();     System.out.println(userSoftRef.get());   } }

若是虚拟机不加参数设置堆内存限制,则打印出:

[id=1,name=gim] After GC: [id=1,name=gim] [id=1,name=gim]

由于虚拟机的内存总量是九十多兆,且虚拟机企图使用的最大内存总量是这个的十几倍。故不会出现资源紧张,也不会产生内存溢出现象。

若是使用-Xmx10m运行上述代码,能够获得:

[id=1,name=gim](第一次从软引用中获取数据) After GC: [id=1,name=gim](GC没有清除软引用) null(因为内存紧张,软引用被清除)

结论:GC未必会回收软引用的对象,可是当内存资源紧张时,软引用对象会被回收,因此软引用对象不会引发内存溢出。

3 弱引用-----发现即回收

弱引用是一种比软引用较弱的引用类型。在系统GC时,只要发现弱引用,无论系统堆空间使用状况如何,都会将对象进行回收。可是因为垃圾回收器的优先级一般很低,所以,并不必定能很快的发现持有弱引用的对象。在这种状况下,弱引用对象能够存在较长的时间,一旦一个弱引用对象被垃圾回收器回收,便会加入一个注册的引用队列(这一点和软引用很像)。弱引用使用java.lang.ref.WeakReference类实现。

实例2:展现弱引用的特色

package com.jvm; import java.lang.ref.WeakReference; public class WeakRef {   public static class User{     public User(int id,String name){       this.id = id;       this.name = name;     }     public int id;     public String name;

    @Override     public String toString() {       return "[id="+String.valueOf(id)+",name="+name+"]";     }   }

  public static void main(String[] args) {     User u = new User(1,"gim");     WeakReference<User> userReference = new WeakReference<User>(u);     u = null;     System.out.println(userReference.get());     System.gc();     System.out.println("After GC:");     System.out.println(userReference.get());   } }

不用设置虚拟机参数,直接运行代码打印出:

[id=1,name=gim] After GC: null

能够看书,GC以后,弱引用对象当即被清除。弱引用和软引用同样,在构造弱引用时,也能够指定一个引用队列,当弱引用对象被回收时,就会加入指定的引用队列,经过这个队列能够跟踪对象的回收状况。

注意:软引用、弱引用都很是适合来保存那些无关紧要的缓存数据(应用场景)。若是这么作,当系统内存不足时,这些缓存数据会被回收,不会致使内存的溢出,而当内存资源充足时,这些缓存又能够存在至关长的时间,从而起到加速系统的做用。

4 虚引用----对象回收跟踪

一个持有虚引用的对象,和没有引用几乎同样。随时均可能被垃圾回收器回收,当试图经过虚引用的get()方法取得强引用时,老是会失败。

而且,虚引用必须和引用队列一块儿使用,它的做用主要是跟踪垃圾回收过程

当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知引用程序对象的回收状况。

实例3 :使用虚引用跟踪一个可复活对象的回收

package com.jvm;

import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue;

public class TraceCanReliveObj {   public static TraceCanReliveObj obj; //能够复活对象   static ReferenceQueue<TraceCanReliveObj> phantomQueue = null;   public static class CheckRefQueue extends Thread{     @Override     public void run() {       while(true){         if(phantomQueue!=null){           PhantomReference<TraceCanReliveObj> objt = null;           try{             objt = (PhantomReference<TraceCanReliveObj>) phantomQueue.remove();           }catch(InterruptedException e){             e.printStackTrace();           }           if(objt!=null){             System.out.println("TraceCanReliveObj is delete by GC");           }         }       }     }   }

  @Override   protected void finalize() throws Throwable {     super.finalize();     System.out.println("CanReliveObj finalize called");     obj = this; //使得对象复活   }   @Override   public String toString() {     return "I am CanReliveObj";   }   public static void main(String[] args) throws InterruptedException {     Thread t = new CheckRefQueue();     t.setDaemon(true);//守护线程     t.start();     phantomQueue = new ReferenceQueue<TraceCanReliveObj>();     obj = new TraceCanReliveObj();

    //构造了TraceCanReliveObj对象的虚引用,并指定了引用队列。     PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj, phantomQueue);     obj = null; //将强引用去除     System.gc(); //第一次进行GC,因为对象可复活,GC没法回收该对象。     Thread.sleep(1000);     if(obj==null){       System.out.println("obj 是 null");     }else{       System.out.println("obj 可用");     }     System.out.println("第二次GC");     obj = null;     System.gc(); //第二次进行GC,因为finalize()只会被调用一次,所以第2次GC会回收对象,同时其引用队列应该也会捕获到对象的回收     Thread.sleep(1000);     if(obj==null){       System.out.println("obj 是 null");     }else{       System.out.println("obj 可用");     }   } }

直接运行代码打印:

CanReliveObj finalize called (对象复活) obj 可用 第二次GC           (第2次,对象没法复活)  TraceCanReliveObj is delete by GC (引用队列捕获到对象被回收) obj 是 null

因为虚引用能够跟踪对象的回收时间,所以,也能够将一些资源释放操做放置在虚引用中执行和记录。

相关文章
相关标签/搜索