在Java1.2中我们可以发现一个java.lang.ref包,在这个包中我们可以发现有关引用的知识,比如WeakReference弱引用和SoftReference强引用。
弱引用(WeakReference):
只具有弱引用的对象声明周期更短暂,在垃圾回收期线程扫描它所管辖的内存区域的过程中,一旦发现了只具有若引用的对象,不管当前内存空间是否足够,都会回收它的内存,不过,要注意的是,由于垃圾回收期是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
软引用(SoftReference):
也称为强引用,如果一个对象只具有软引用,则内存空间足够,垃圾回收期就不会回收它,如果内存空间不足了,就会回收这些对象的内存,如果垃圾回收期没有回收它,该对象就可以被程序使用。
WeakReference和SoftReference的用武之地:
WeakReference通常用于在某处保存对象的引用,而又不干扰该对对象被GC回收,如:用于Debug、内存监视工具等程序中。因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。
SoftReference是强引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。
下面通过一个实例,来展现这两种引用如何保存对象实例的:
User.java:
public class User implements Serializable{ private static final long serialVersionUID = 1L; /* 用户id */ private Integer uid; /* 用户名 */ private String uname; public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } }
WeakReferenceTest.java:
public class WeakReferenceTest { public static void main(String[] args) { /*创建User对象*/ User user = new User(); /*设置username*/ user.setUname("廖泽民"); /*把对象放在弱引用中*/ WeakReference<User> weak = new WeakReference<User>(user); /*把user对象置空,然后再从若引用中取值*/ user = null; int i = 0; /*weak.get()表示从引用中取得对象*/ while (weak.get() != null) { System.out.println(String.format("从弱引用中取值: %s, count: %d", weak.get().getUname(), ++i)); if (i % 10 == 0) { System.gc(); System.out.println("内存回收方法被调用"); } try { Thread.sleep(500); } catch (Exception e) { } } System.out.println("对象已经被JVM回收"); } }
运行的结果:
从运行结果,我们可以发现当把对象实例保存到WeakReference后,再将对象置空,然后从WeakReference中取值,当System.gc()方法被调用后,对象实例也会被回收!
SoftReferenceTest.java:
public class SoftReferenceTest { public static void main(String[] args) { /* 创建User对象 */ User user = new User(); /* 设置用户名 */ user.setUname("廖泽民"); /* 创建强引用对象 */ SoftReference<User> soft = new SoftReference<User>(user); /* 把user对象置空,然后再从强引用中取值【注:要先存在引用中再置空,注意顺序啊】 */ user = null; int i = 0; while (soft.get() != null) { System.out.println(String.format("从强引用中获取对象: %s, count: %d", soft.get().getUname(), ++i)); if (i % 10 == 0) { System.gc(); System.out.println("内存回收方法被调用!"); } try { Thread.sleep(500); } catch (InterruptedException e) { } } System.out.println("对象已经被JVM回收!"); } }
运行结果:
从运行结果(程序不会停止,一直执行)可以发现,我们把对象实例保存到SoftReference中,然后将对象置空,再从SoftReference中取值时,即使显示的调用System.gc();方法,该对象实例也不会被回收(除非发生内存溢出,该对象才会被回收)