Reference和Reference Queue

起因

看guaua cache的时候看到源码里面有用到softReference、weakReference等作为key和value,突然想要深入理解一下引用的原理,看了源码和不少文章,终于有点感觉了,主要是gc控制了一些参数的设置,所以感觉有点难理解。关于基本的知识,我就不赘述了,反正我也是看的大家的文章?

源码和流程

大部分网上的文章都是差不多贴的源码注释:

/* A Reference instance is in one of four possible internal states:
     *
     *     Active: Subject to special treatment by the garbage collector.  Some
     *     time after the collector detects that the reachability of the
     *     referent has changed to the appropriate state, it changes the
     *     instance's state to either Pending or Inactive, depending upon
     *     whether or not the instance was registered with a queue when it was
     *     created.  In the former case it also adds the instance to the
     *     pending-Reference list.  Newly-created instances are Active.
     *
     *     Pending: An element of the pending-Reference list, waiting to be
     *     enqueued by the Reference-handler thread.  Unregistered instances
     *     are never in this state.
     *
     *     Enqueued: An element of the queue with which the instance was
     *     registered when it was created.  When an instance is removed from
     *     its ReferenceQueue, it is made Inactive.  Unregistered instances are
     *     never in this state.
     *
     *     Inactive: Nothing more to do.  Once an instance becomes Inactive its
     *     state will never change again.
     *
     * The state is encoded in the queue and next fields as follows:
     *
     *     Active: queue = ReferenceQueue with which instance is registered, or
     *     ReferenceQueue.NULL if it was not registered with a queue; next =
     *     null.
     *
     *     Pending: queue = ReferenceQueue with which instance is registered;
     *     next = this
     *
     *     Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
     *     in queue, or this if at end of list.
     *
     *     Inactive: queue = ReferenceQueue.NULL; next = this.
     *
     * With this scheme the collector need only examine the next field in order
     * to determine whether a Reference instance requires special treatment: If
     * the next field is null then the instance is active; if it is non-null,
     * then the collector should treat the instance normally.
     *
     * To ensure that a concurrent collector can discover active Reference
     * objects without interfering with application threads that may apply
     * the enqueue() method to those objects, collectors should link
     * discovered objects through the discovered field. The discovered
     * field is also used for linking Reference objects in the pending list.
     */

注释讲的算比较清楚了,虽然是英文,大家应该可以看懂啦!有个概念就行。网上有个状态转换图,比较清晰:
Reference和Reference Queue_第1张图片
Reference和Reference Queue_第2张图片
上面的图大概描述了一个对象从创建被回收的过程,看了这些好像有点了解了,但是还是有点蒙,我们再看看Reference的成员变量:

referent:被封装的对象
queue:注册的引用队列,只有注册了队列的Reference,才会有pending和enqueued的状态,引用队列的使用可以让我们知道对象什么时候被gc,gc的时候引用会被enqueue到队列里面,我们可以从队列中取出来,或者做一些监控(资源清除等),没有注册队列的reference,我们就一脸懵逼了。
next:在引用队列里面形成一个链表,也就是指向下一个被gc的引用对象,既然next指向引用队列的gc对象,那注释里面说”next != null,说明就是被gc了,收集器应该处理掉他了”就不难理解了。
discovered:这个我觉得是待加入到引用队列里面的一个对象,JVM帮我们设置的,和pending对象联合使用形成一个链表,相当于pending队列的next吧。
pending:static类型的,说明只有一个,也就是pending链表的head了。
ReferenceHandler:一个Thead,而且在static初始化代码里面设置了Thread.MAX_PRIORITY最高优先级,run方法里面就是个死循环

public void run() {
            while (true) {
                tryHandlePending(true);
            }
       }

tryHandlePending这个方法里面主要就是不断取出pending列表里面的引用,并且加入到ReferenceQueue里面去,也就是上图中从pending队列放到了ReferenceQueue队列中了。

reference里面的pending队列和referenceQueue队列都有lock进行控制,不会出现并发问题。

分析完成员变量,不知道大家理解一点了没有,我的表达水平有限,所以想多写写,练练手?。上面两张图还是画的很清楚的?,大家可以结合源码再看看,有什么问题可以交流,帮助我打怪升级。

具体的引用

软引用和弱引用:

软引用是在内存不足的情况下进行回收,那什么时候算内存不足呢?具体的jvm源码也不贴了,大致的计算方式就是:

interval > get_heap_free_at_last_gc * SoftRefLRUPolicyMSPerMB

SoftRefLRUPolicyMSPerMB:每1M空闲空间可保持的SoftReference对象生存的时长(单位毫秒),默认值是1000

interval= timestamp - clock(软引用的两个变量)

意思就是当这个引用被get(访问)的时间足够长,就可以被回收了,而get_heap_free_at_last_gc越大(内存越大,越不会内存不足),或者SoftRefLRUPolicyMSPerMB越大,软引用存活的时间就越长。

软引用会有一定的问题,由于要在内存不足的时候被回收,那如果一直不回收,就会被放到老年代,而老年代会越来越大,导致不停地full gc。

相对软引用,弱引用用的就比较多了,只要gc就会被回收,对JVM没有副作用,对于一般的缓存还挺有效的。

幻引用主要的实现就是Cleaner了,实现资源的回收和释放。

今天就这样了。以后有新发现再说。

参考:

https://www.jianshu.com/p/439a8f738153
https://coldwalker.com/2019/02//gc_intro/

你可能感兴趣的:(java深入学习)