Java弱引用(WeakReference)的理解与使用 java
看到篇帖子, 国外一个技术面试官在面试senior java developer的时候, 问到一个weak reference相关的问题. 他没有指望有人可以完整解释清楚weak reference是什么, 怎么用, 只是指望有人可以提到这个concept和java的GC相关. 很惋惜的是, 20多个拥有5年以上java开发经验的面试者中, 只有两人知道weak reference的存在, 而其中只有一人实际用到过他. 无疑, 在interviewer眼中, 对于weak reference的理解和应用在面试中给了这一个interviewee至关多的加分. 因此, 将我对于这个技术的理解和使用总结在这篇博客里, 但愿读者和本身经过读和写这篇帖子, 可以在之后的工做和面试中得到加分.程序员
在Java里, 当一个对象o被建立时, 它被放在Heap里. 当GC运行的时候, 若是发现没有任何引用指向o, o就会被回收以腾出内存空间. 或者换句话说, 一个对象被回收, 必须知足两个条件: 1)没有任何引用指向它 2)GC被运行.面试
在现实状况写代码的时候, 咱们每每经过把全部指向某个对象的referece置空来保证这个对象在下次GC运行的时候被回收 (能够用java -verbose:gc来观察gc的行为)函数
1 Object c = new Car(); 2 c=null;
可是, 手动置空对象对于程序员来讲, 是一件繁琐且违背自动回收的理念的. 对于简单的状况, 手动置空是不须要程序员来作的, 由于在java中, 对于简单对象, 当调用它的方法执行完毕后, 指向它的引用会被从stack中popup, 因此他就能在下一次GC执行时被回收了.oop
可是, 也有特殊例外. 当使用cache的时候, 因为cache的对象正是程序运行须要的, 那么只要程序正在运行, cache中的引用就不会被GC给(或者说, cache中的reference拥有了和主程序同样的life cycle). 那么随着cache中的reference愈来愈多, GC没法回收的object也愈来愈多, 没法被自动回收. 当这些object须要被回收时, 回收这些object的任务只有交给程序编写者了. 然而这却违背了GC的本质(自动回收能够回收的objects).优化
因此, java中引入了weak reference. 相对于前面举例中的strong reference:this
1 Object c = new Car(); //只要c还指向car object, car object就不会被回收
当一个对象仅仅被weak reference指向, 而没有任何其余strong reference指向的时候, 若是GC运行, 那么这个对象就会被回收. weak reference的语法是:spa
1 WeakReference<Car> weakCar = new WeakReference(Car)(car);
当要得到weak reference引用的object时, 首先须要判断它是否已经被回收:.net
1 weakCar.get();
若是此方法为空, 那么说明weakCar指向的对象已经被回收了.code
下面来看一个例子:
1 package weakreference; 2 /** 3 * @author wison 4 */ 5 public class Car { 6 private double price; 7 private String colour; 8 9 public Car(double price, String colour){ 10 this.price = price; 11 this.colour = colour; 12 } 13 14 public double getPrice() { 15 return price; 16 } 17 public void setPrice(double price) { 18 this.price = price; 19 } 20 public String getColour() { 21 return colour; 22 } 23 public void setColour(String colour) { 24 this.colour = colour; 25 } 26 27 public String toString(){ 28 return colour +"car costs $"+price; 29 } 30 31 }
在上例中, 程序运行一段时间后, 程序打印出"Object has been collected." 说明, weak reference指向的对象的被回收了.
值得注意的一点 , 即便有 car 引用指向对象, 且 car 是一个strong reference, weak reference weakCar指向的对象仍然被回收了. 这是由于java的编译器在发现进入while循环以后, car 已经没有被使用了, 因此进行了优化(将其置空?). 当把TestWeakReference.java修改成:
1 package weakreference; 2 3 import java.lang.ref.WeakReference; 4 5 /** 6 * @author wison 7 */ 8 public class TestWeakReference { 9 10 public static void main(String[] args) { 11 12 Car car = new Car(22000,"silver"); 13 WeakReference<Car> weakCar = new WeakReference<Car>(car); 14 15 int i=0; 16 17 while(true){ 18 System.out.println("here is the strong reference 'car' "+car); 19 if(weakCar.get()!=null){ 20 i++; 21 System.out.println("Object is alive for "+i+" loops - "+weakCar); 22 }else{ 23 System.out.println("Object has been collected."); 24 break; 25 } 26 } 27 } 28 29 }
weak reference指向的object就不会被回收了. 由于还有一个strong reference car 指向它.
* WeakReference的一个特色是它什么时候被回收是不可肯定的, 由于这是由GC运行的不肯定性所肯定的. 因此, 通常用weak reference引用的对象是有价值被cache, 并且很容易被从新被构建, 且很消耗内存的对象.
在weak reference指向的对象被回收后, weak reference自己其实也就没有用了. java提供了一个ReferenceQueue来保存这些所指向的对象已经被回收的reference. 用法是在定义WeakReference的时候将一个ReferenceQueue的对象做为参数传入构造函数.
-SoftReference
soft reference和weak reference同样, 但被GC回收的时候须要多一个条件: 当系统内存不足时(GC是如何断定系统内存不足? 是否有参数能够配置这个threshold?), soft reference指向的object才会被回收. 正由于有这个特性, soft reference比weak reference更加适合作cache objects的reference. 由于它能够尽量的retain cached objects, 减小重建他们所需的时间和消耗.