初学Java,在读Thinking in Java时碰到个小问题:一个例子,请注意,在main函数里有两次checkQueue()的调用:class VeryBig {
private String id;
public VeryBig(String id) {
this.id = id;
}
protected void finalize() {
System.out.println("Finalizing " + id);
}
}public class ReferenceTest {

private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();
public static void checkQueue() {
Reference<? extends VeryBig> ref = rq.poll();
if (ref != null) {
System.out.println("In queue: " + ref.get());
}
} public static void main(String args[]) {
int size = 1;
LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
for (int i = 0; i < size; i++) {
weakList.add(new WeakReference<VeryBig>(new VeryBig("Weak " + i), rq));
System.out.println("Just created weak: " + weakList.getLast());
checkQueue();
}

System.gc();

LinkedList<PhantomReference<VeryBig>> phantomList = new LinkedList<PhantomReference<VeryBig>>();
for (int i = 0; i < size; i++) {
phantomList.add(new PhantomReference<VeryBig>(new VeryBig("Phantom " + i), rq));
System.out.println("Just created phantom: " + phantomList.getLast());
checkQueue();
}
}
}
输出是:Just created weak: java.lang.ref.WeakReference@c17164
Just created phantom: java.lang.ref.PhantomReference@61de33
Finalizing Weak 0
In queue: null
重点在这里,我知道WeakReference的构造函数也可以不带第二个参数,也就是ReferenceQueue的参数,于是代码我稍作改动,只把main函数里构造WeakReference时的 rq 参数去掉,别的一点也不改。代码: public static void main(String args[]) {
int size = 1;
LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
for (int i = 0; i < size; i++) {
weakList.add(new WeakReference<VeryBig>(new VeryBig("Weak " + i)));
System.out.println("Just created weak: " + weakList.getLast());
checkQueue();
} System.gc();

LinkedList<PhantomReference<VeryBig>> phantomList = new LinkedList<PhantomReference<VeryBig>>();
for (int i = 0; i < size; i++) {
phantomList.add(new PhantomReference<VeryBig>(new VeryBig("Phantom " + i), rq));
System.out.println("Just created phantom: " + phantomList.getLast());
checkQueue();
}
}
输出:Just created weak: java.lang.ref.WeakReference@c17164
Just created phantom: java.lang.ref.PhantomReference@61de33
Finalizing Weak 0
"In queue" 那句log没有了~~~第二次修改,把PhantomReference的部分从main函数里删除,保留WeakReference的rq参数,现在checkQueue只执行一次了: public static void main(String args[]) {
int size = 1;
LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
for (int i = 0; i < size; i++) {
weakList.add(new WeakReference<VeryBig>(new VeryBig("Weak " + i), rq));
System.out.println("Just created weak: " + weakList.getLast());
checkQueue();
}
System.gc();
}
输出:Just created weak: java.lang.ref.WeakReference@c17164
Finalizing Weak 0
"In queue" 那句log还是没有~~~~问题:
1,WeakReference在构造时带 ReferenceQueue和不带的时候,在行为上有什么区别?
2,帮解释下这三个测试的结果,越详细越好,LZ已经很晕了,把LZ当白痴好了~
3,ReferenceQueue和WeakReference以及SoftReference的使用机制,同样把LZ当白痴好了。。我上网找了些资料,不够通俗易懂,看得我还是很晕,希望找个高手用通俗的语言给解释一下先谢过了!

解决方案 »

  1.   

    java的引用分为:强引用、软引用、弱引用、虚引用
    1. 强引用(StrongReference)
    强引用即我们平时使用的。一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止。
    2. 软引用(SoftReference)
    如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
    3.弱引用(WeakReference)
    在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
    4虚引用(PhantomReference)
    虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。再来看引用队列:
    如果软引用、弱引用、虚引用在new时添加了对应的ReferenceQueue,那么gc在回收掉Reference对应的对象后,会将Reference放置对应的队列,但是此时Reference关联的对象已经被gc回收,此时Reference.get总是返回null的。主要是理解Reference和Reference包装的对象,gc是回收了Reference的包装的对象,但是Reference本身也是一个对象。lz的例子不是很能说明问题,改动一下:package cn.leisore.daily._2011_05_01;import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.WeakReference;
    import java.util.LinkedList;public class ReferenceTest { private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>(); public static void checkQueue() {
    Reference<? extends VeryBig> ref = null;
    while ((ref = rq.poll()) != null) {
    if (ref != null) {
    System.out.println("In queue: "
    + ((VeryBigWeakReference) (ref)).id);
    }
    }
    } public static void main(String args[]) {
    int size = 3;
    LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
    for (int i = 0; i < size; i++) {
    weakList
    .add(new VeryBigWeakReference(new VeryBig("Weak " + i), rq));
    System.out.println("Just created weak: " + weakList.getLast()); } System.gc();
    checkQueue();
    }
    }class VeryBig {
    public String id;
    byte[] b = new byte[2 * 1024]; public VeryBig(String id) {
    this.id = id;
    } protected void finalize() {
    System.out.println("Finalizing VeryBig " + id);
    }
    }class VeryBigWeakReference extends WeakReference<VeryBig> {
    public String id; public VeryBigWeakReference(VeryBig big, ReferenceQueue<VeryBig> rq) {
    super(big, rq);
    this.id = big.id;
    } protected void finalize() {
    System.out.println("Finalizing VeryBigWeakReference " + id);
    }
    }
    //输出:
    Just created weak: cn.leisore.daily._2011_05_01.VeryBigWeakReference@1fb8ee3
    Just created weak: cn.leisore.daily._2011_05_01.VeryBigWeakReference@61de33
    Just created weak: cn.leisore.daily._2011_05_01.VeryBigWeakReference@14318bb
    Finalizing VeryBig Weak 2
    Finalizing VeryBig Weak 1
    Finalizing VeryBig Weak 0或ust created weak: cn.leisore.daily._2011_05_01.VeryBigWeakReference@1fb8ee3
    Just created weak: cn.leisore.daily._2011_05_01.VeryBigWeakReference@61de33
    Just created weak: cn.leisore.daily._2011_05_01.VeryBigWeakReference@14318bb
    Finalizing VeryBig Weak 2
    Finalizing VeryBig Weak 1
    Finalizing VeryBig Weak 0
    In queue: Weak 1
    In queue: Weak 2
    In queue: Weak 0可以看到,第二个输出其实是最符合我的理解的,gc回收了三个VeryBig,然后把对应的WeakReference放至队列中。但是第一个输出我也没明白为什么。但是如果byte[] b = new byte[2 * 1024 * 1024];
    在我的机器上就始终是第二个输出了。
      

  2.   

    WeakReference在构造时带 ReferenceQueue的时候 如果对象被gc回收 那么就会在ReferenceQueue里放置对应的Reference 这样的话 三个输入是可以解释的
    第一次 In queue是在gc后的那句check里输出的 
    第二次 没有带参数 所以不会输出
    第三次 gc后没有check 所以不输出
      

  3.   

    我觉得不太对,我试了一下在第三次的代码最后gc后调用checkQueue(),那个In queue的log还是没有,这又怎么解释呢?代码: public static void main(String args[]) {
    int size = 1;
    LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
    for (int i = 0; i < size; i++) {
    weakList.add(new WeakReference<VeryBig>(new VeryBig("Weak " + i), rq));
    System.out.println("Just created weak: " + weakList.getLast());
    checkQueue();
    }
    System.gc();

    checkQueue(); //check queue after gc.
    }
    // Output:
    Just created weak: java.lang.ref.WeakReference@c17164
    Finalizing Weak 0
      

  4.   

    这点我也发现了 但是没找到一个合理的解释 
    开始考虑是GC回收和创建rq对象之间有时差 但是好像也不是很对