可能涉及到 JDK的两个 Bug,但也是一个几乎不会遇到的场景,记录一下,只能说一入 Java 深似海,大周末的,图个乐呵。
关于 Java 弱引用和引用队列,我们一般是这么认为的,这里引用网上一篇博客的说明:
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
但是经过一些代码测试和考证,是发现这个结论不一定成立的。
首先看一下 java.lang.ref.Reference#enqueue
方法:
/**
* Adds this reference object to the queue with which it is registered,
* if any.
*
* <p> This method is invoked only by Java code; when the garbage collector
* enqueues references it does so directly, without invoking this method.
*
* @return <code>true</code> if this reference object was successfully
* enqueued; <code>false</code> if it was already enqueued or if
* it was not registered with a queue when it was created
*/
public boolean enqueue() {
return this.queue.enqueue(this);
}
大意就是说这个方法虽然是用 Java 代码写的,但是垃圾收集器会直接调用。
再看 java.lang.ref.Reference#isEnqueued
方法:
/**
* Tells whether or not this reference object has been enqueued, either by
* the program or by the garbage collector. If this reference object was
* not registered with a queue when it was created, then this method will
* always return <code>false</code>.
*
* @return <code>true</code> if and only if this reference object has
* been enqueued
*/
public boolean isEnqueued() {
return (this.queue == ReferenceQueue.ENQUEUED);
}
也就是说这个方法返回 true,就说明引用对象入队了。
代码测试:
package dongguabai.spring.mu;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* @author Dongguabai
* @description
* @date 2021-03-06 21:50
*/
public class ReferenceDemo {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
m1();
}
}
private static void m1() {
ReferenceQueue referenceQueue = new ReferenceQueue();
Reference<String> weakReference = new WeakReference<>(new String("abcd"),referenceQueue);
System.out.println("before gc。。。");
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println(weakReference.isEnqueued());
System.gc();
System.out.println("after gc。。。");
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println(weakReference.isEnqueued());
System.out.println("-------------------");
}
}
代码流程很简单,就是想模拟出弱引用对象被回收后入队的情况,但是输出结果:
before gc。。。
abcd
null
false
after gc。。。
null
null
true
-------------------
before gc。。。
abcd
null
false
after gc。。。
null
java.lang.ref.WeakReference@61bbe9ba
false
-------------------
before gc。。。
abcd
null
false
after gc。。。
null
java.lang.ref.WeakReference@610455d6
false
-------------------
...
...
发现了两个问题:
- 弱引用对象在 GC 后不一定会入队(经过不严谨的多次测试,是第一次不会入队);
- 同时
java.lang.ref.Reference#isEnqueued
方法的注释说反了,应该是没入队会返回 true
;
当然这两个问题也已经有人反馈了,这里是相关链接:
- https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8052260
- https://bugs.java.com/bugdatabase/view_bug.do?bug_id=5047199
最后做一个总结:
- 代码还是实践出真知;
- 如果要使用虚引用和引用队列做一些生产者/消费者的行为,最好严格测试;