在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();方法,该对象实例也不会被回收(除非发生内存溢出,该对象才会被回收)