1.finalizer生产大于消费导致内存溢出测试
User重写了finalize方法的类在创建User对象的同时会创建一个Finalizer对象并加入到Finalizer中以unfinalized为头节点的链表中,当触发GC时如果垃圾收集器判断User对象没有与GCRoot的强引用时则会将Finalizer放到Reference.pending属性上,由线程ReferenceHandler负责处理将Finalizer添加到Finalizer.queue中,然后由线程FinalizerThread负责处理,将Finalizer从Finalizer中以unfinalized为头节点的链表中移除,且调用Finalizer.queue.remove()将Finalizer从Finalizer.queue中移除再执行User中重写的finalize方法再讲Finalizer中的referent属性置空与User切断引用。
此时:
User对象:没有任何引用;
User对应的Finalizer对象:没有任何引用,其各个属性为 next(Finalizer)=this;prev=this;referent=null;queue=ReferenceQueue.NULL;next(Reference)=this; discovered=null;
需要再次触发GC才会回收Finalizer对象和User对象,如果没有触发新的GC则Finalizer对象和User对象会一直存在堆上。
2.WeakReference回收测试(关注pending状态时机)
使用WeakReference添加User引用,当触发GC时如果垃圾收集器判断User对象没有与GCRoot的强引用时将WeakReference放到Reference.pending属性上(此时WeakReference中的referent属性即User已经被回收掉了),由线程ReferenceHandler负责处理将WeakReference添加到创建WeakReference时传入的queue里。由于WeakReference对象本身还没有被回收切被queue强引用着,所以需要手动遍历queue切断WeakReference的强引用(WeakHashMap的原理)。
3.SoftReference回收测试(关注pending状态时机)
使用SoftReference添加User引用,当触发GC时如果垃圾收集器判断User对象没有与GCRoot的强引用,且JVM内存不足时(必须进行fullGC)将SoftReference放到Reference.pending属性上(此时SoftReference中的referent属性即User已经被回收掉了),由线程ReferenceHandler负责处理将SoftReference添加到创建SoftReference时传入的queue里。由于SoftReference对象本身还没有被回收切被queue强引用着,所以需要手动遍历queue切断SoftReference的强引用。
4.WeakReference和finalizer组合测试
当User重写了finalize方法其使用WeakReference添加了User的引用,当User在外部再无强引用时,垃圾回收器会将WeakReference(垃圾回收器将referent置空)和Finalizer放到Reference.pending(WeakReference和Finalizer的处理顺序无法保证,是由垃圾回收器决定的,不同垃圾回收器可能不一致)属性上,由ReferenceHandler进行处理,后续过程与1、2一致;
5.PhantomReference虚引用回收测试(关注pending状态时机和Cleaner测试)
// TODO待补充
Reference中存在Active/活跃状态、Pending/等待状态、Enqueued/已入列状态(排队状态)、Inactive/不活跃状态四种状态
Active
当Reference对象刚刚被创建且没有发生GCROOT检查时。
此时Reference各个属性状态:
referent=User对象
queue=创建Reference时传入的ReferenceQueue,未传入为ReferenceQueue.NULL
next=null
discovered=null
static pending=null
Pending
当触发可达性分析对对象进行GCRoot检测时,垃圾回收器会关注全部的Reference对象将referent属性GCRoot状态发生变化(不同的引用类型定义不同)的Reference放到一个队列A里,垃圾回收器遍历队列对Reference进行指定操作(不同的引用类型定义不同),并把Reference实例放置到Reference.pending属性上(静态),将队列中的下一个Reference实例放到当前Reference的discovered属性上,next赋值为this。
referent属性GCRoot状态发生变化(不同的引用类型定义不同):WeakReference和PhantomReference和FinalReference是referent没有强引用时;SoftReference是referent没有强引用且内存不足时。
指定操作(不同的引用类型定义不同):WeakReference和SoftReference和PhantomReference 将referent置为null并回收User;FinalReference不做特殊处理。
此时Reference各个属性状态:
referent= WeakReference和SoftReference和PhantomReference时referent=null;FinalReference时referent=User;
queue=创建Reference时传入的ReferenceQueue,未传入为ReferenceQueue.NULL
next=this/A[x]
discovered=A[x].next;
static pending=A[x]
Enqueued
Reference中有一个ReferenceHandler线程负责异步处理垃圾处理器放到Reference.pending上的Reference实例,如果这个Reference在创建时有传入ReferenceQueue即Reference.queue!=ReferenceQueue.NULL则将Reference实例添加到创建时传入的ReferenceQueue中。
此时Reference各个属性状态:
referent= WeakReference和SoftReference和PhantomReference时referent=null;FinalReference时referent=User;
queue=ReferenceQueue.ENQUEUED
next=上一个添加到创建时传入的ReferenceQueue中的Reference实例
discovered=null;
static pending=this.discovered;
Inactive
当调用创建Reference时传入的ReferenceQueue的poll()或者remove()方法时(一般定时调用ReferenceQueue的poll方法获取Enqueued状态的Reference来做对应处理),Reference会变为Inactive状态;即意味着此引用对象可以被回收,并且对内部封装的对象也可以被回收掉了。
此时Reference的各个属性状态:
referent= WeakReference和SoftReference和PhantomReference时referent=null;FinalReference时referent=null(Finalizer线程会显式置空referent);
queue=ReferenceQueue.NULL
next=this;
discovered=null;
static pending=此属性为静态属性可能受其他Reference和垃圾回收器影响变化。
JVM的Reference中的queue和pending等属性以及四种状态机制主要目的是用于JVM垃圾回收器线程和用户线程之间的通信,要知道线程通信的两种手段是共享内存和消息传递,JVM线程明显不能给用户线程发送消息,所以这是一种共享内存机制。JVM的垃圾回收器线程会操作pending属性将referent属性GCRoot状态发生变化的Reference实例放到pending属性上,在java中有一个线程ReferenceHandler对pending属性进行轮询监控处理放入queue中,然后用户线程可以通过变量queue获取到GC线程的通知。