ThreadLocal源码
在看ThreadLocal源码的时候,其中嵌套类ThreadLocalMap中的Entry继承了WeakReferenc
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
下面进入正题,WeakReference如字面意思,弱引用, 当一个对象仅仅被weak reference(弱引用)指向, 而没有任何其他strong reference(强引用)指向的时候, 如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。
从认识WeakReference开始
弱引用对象,它不阻止它们的引用对象被设定为 finalizable, finalized, and reclaimed。弱引用最常用于实现规范化映射。假设垃圾收集器在某个时间点确定一个对象是弱可达的。此时,它将自动清除对该对象的所有弱引用,以及对任何其他弱可达对象的所有弱引用,而该对象可通过强和软引用链访问。同时,它将声明所有以前弱可达的对象为可终结的。在同一时间或稍后的某个时间,它将对那些注册在引用队列中的新清除的弱引用进行排队。
public class WeakReference<T> extends Reference<T> {
/**
* Creates a new weak reference that refers to the given object. The new
* reference is not registered with any queue.
*
* @param referent object the new weak reference will refer to
*/
public WeakReference(T referent) {
super(referent);
}
/**
* Creates a new weak reference that refers to the given object and is
* registered with the given queue.
*
* @param referent object the new weak reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
引用对象的抽象基类。该类定义了所有引用对象的通用操作。因为引用对象是与垃圾收集器密切合作实现的,所以不能直接对该类进行子类化。
public abstract class Reference<T> {
private T referent; /* Treated specially by GC */
//要注册引用的队列,或null如果不需要注册
volatile ReferenceQueue<? super T> queue;
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
* 四种状态
*/
@SuppressWarnings("rawtypes")
Reference next;
/* When active: next element in a discovered reference list maintained by GC (or this if last)
* pending: next element in the pending list (or null if last)
* otherwise: NULL
*/
transient private Reference<T> discovered; /* used by VM */
/**
* 返回该引用对象的referent。如果该引用对象已经被清除,或者被程序或垃圾收集器清除,然后此方法返null
*/
public T get() {
return this.referent;
}
}
/*
* pending: next element in the pending list (or null if last)
* otherwise: NULL
*
*/
public void clear() {
this.referent = null;
}
关于弱引用的测试
通过finalize 自救,再次对finalize对象赋值,来保证此次gc不会回收对象,但是finalize方法只会执行一次
public class TestFinalize {
private static TestFinalize finalize;
/**
* 覆盖finalize,在回收的时候会执行。
* 只会执行一次
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("TestFinalize: 执行 finalize 方法");
finalize=this;
}
public static void main(String[] args) throws InterruptedException {
TestFinalize.finalize = new TestFinalize();
finalize=null;
//手动触发一次gc
System.gc();
Thread.sleep(3000);
System.out.println("第一次gc后"+ TestFinalize.finalize);
finalize=null;
System.gc();
Thread.sleep(3000);
System.out.println("第二次gc后"+ TestFinalize.finalize);
}
}
entry.get()=null,仅仅回收被弱引用的对象,而不是回收弱引用对象,如下使用弱引用引用对象
public class NullReference {
private String name;
public NullReference() {
}
public NullReference(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("TestFinalize: 执行 finalize 方法 name:"+name);
}
@Override
public String toString() {
return "NullReference{" +
"name='" + name + '\'' +
'}' + ", hashCode:" + this.hashCode();
}
}
/**
* 这个时候 NullReference 就成为了 被弱引用的对象
* 在弱引用回收的时候会回收 NullReference 而不是回收WeakReference 的子类TestWeakReference
*/
public class TestWeakReference extends WeakReference<NullReference> {
public TestWeakReference(NullReference referent) {
super(referent);
}
public static void main(String[] args) {
TestWeakReference testWeakReference = new TestWeakReference(new NullReference("被弱引用的对象"));
//通过WeakReference的get()方法获取Apple
System.out.println("referent:" + testWeakReference.get());
//被弱引用的对象 GC直接回收,其他强引用也可以访问
System.gc();
try {
//休眠一下,在运行的时候加上虚拟机参数v,输出gc信息,确定gc发生了。
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果为空,代表被回收了
if (testWeakReference.get() == null) {
System.out.println("clear referent。");
}
System.out.println("TestWeakReference:" + testWeakReference);
}
}
测试输出结果如下:
"C:\Program Files\Java\jdk1.8.0_341\bin\java.exe" -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.2\lib\idea_rt.jar=64914:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_341\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\rt.jar;C:\project\fairy-letCode\target\classes;C:\Users\EHUALUL\.m2\local_repo\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar" com.fairy.letCode.threadLocal.TestWeakReference
referent:NullReference{name='被弱引用的对象'}, hashCode:1956725890
[GC (System.gc()) [PSYoungGen: 3932K->496K(76288K)] 3932K->504K(251392K), 0.0010511 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 496K->0K(76288K)] [ParOldGen: 8K->367K(175104K)] 504K->367K(251392K), [Metaspace: 3138K->3138K(1056768K)], 0.0039893 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
TestFinalize: 执行 finalize 方法 name:被弱引用的对象
clear referent。
TestWeakReference:com.fairy.letCode.threadLocal.TestWeakReference@1540e19d
Heap
PSYoungGen total 76288K, used 4587K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
eden space 65536K, 7% used [0x000000076ab00000,0x000000076af7afe0,0x000000076eb00000)
from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
to space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
ParOldGen total 175104K, used 367K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
object space 175104K, 0% used [0x00000006c0000000,0x00000006c005bca0,0x00000006cab00000)
Metaspace used 3236K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 356K, capacity 388K, committed 512K, reserved 1048576K
Process finished with exit code 0
测试使用ReferenceQueue,会将回收的弱引用对象注册到Queue
public class TestWeakReferntQueue {
public static void main(String[] args) {
ReferenceQueue<NullReference> appleReferenceQueue = new ReferenceQueue<>();
WeakReference<NullReference> appleWeakReference = new WeakReference<>(new NullReference("青苹果"), appleReferenceQueue);
WeakReference<NullReference> appleWeakReference2 = new WeakReference<>(new NullReference("毒苹果"), appleReferenceQueue);
System.out.println("=====gc调用前=====");
Reference<? extends NullReference> reference = null;
while ((reference = appleReferenceQueue.poll()) != null ) {
//不会输出,因为没有回收被弱引用的对象,并不会加入队列中
System.out.println(reference);
}
System.out.println(appleWeakReference);
System.out.println(appleWeakReference2);
System.out.println(appleWeakReference.get());
System.out.println(appleWeakReference2.get());
System.out.println("=====调用gc=====");
System.gc();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====gc调用后=====");
//下面两个输出为null,表示对象被回收了
System.out.println(appleWeakReference.get());
System.out.println(appleWeakReference2.get());
//输出结果,并且就是上面的appleWeakReference、appleWeakReference2,再次证明对象被回收了
Reference<? extends NullReference> reference2 = null;
while ((reference2 = appleReferenceQueue.poll()) != null ) {
//如果使用继承的方式就可以包含其他信息了
System.out.println("appleReferenceQueue中:" + reference2);
}
}
}
测试输出结果如下:
"C:\Program Files\Java\jdk1.8.0_341\bin\java.exe" -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.2\lib\idea_rt.jar=64947:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_341\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_341\jre\lib\rt.jar;C:\project\fairy-letCode\target\classes;C:\Users\EHUALUL\.m2\local_repo\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar" com.fairy.letCode.threadLocal.TestWeakReferntQueue
=====gc调用前=====
java.lang.ref.WeakReference@74a14482
java.lang.ref.WeakReference@1540e19d
NullReference{name='青苹果'}, hashCode:1735600054
NullReference{name='毒苹果'}, hashCode:21685669
=====调用gc=====
[GC (System.gc()) [PSYoungGen: 3932K->464K(76288K)] 3932K->472K(251392K), 0.0010516 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 464K->0K(76288K)] [ParOldGen: 8K->388K(175104K)] 472K->388K(251392K), [Metaspace: 3143K->3143K(1056768K)], 0.0040658 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
TestFinalize: 执行 finalize 方法 name:毒苹果
TestFinalize: 执行 finalize 方法 name:青苹果
=====gc调用后=====
null
null
appleReferenceQueue中:java.lang.ref.WeakReference@74a14482
appleReferenceQueue中:java.lang.ref.WeakReference@1540e19d
Heap
PSYoungGen total 76288K, used 4587K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
eden space 65536K, 7% used [0x000000076ab00000,0x000000076af7afd0,0x000000076eb00000)
from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
to space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
ParOldGen total 175104K, used 388K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
object space 175104K, 0% used [0x00000006c0000000,0x00000006c0061178,0x00000006cab00000)
Metaspace used 3237K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 356K, capacity 388K, committed 512K, reserved 1048576K
Process finished with exit code 0
从测试结果可以看到Gc后,如果被引用的弱引用对象的entry.get()=null, 会被回收,而这些弱引用对象会注册到queue中