guava中的FinalizableReferenceQueue解析

1 引用队列的监控和回调
当创建一个引用队列时,我们需要对这个队列进行监控,即开启一个新的线程来循环判断此队列信息,并从中获取相应的数据信息。在guava中,也是通过创建一个线程然后在循环中进行判断,如下所示(类com.google.common.base.internal.Finalizer):

    Thread thread = new Thread(finalizer);
    thread.setName(Finalizer.class.getName());
    thread.setDaemon(true);

    thread.start();

即创建一个以finalizer runnable对象的线程,然后启动之,为避免阻止进程结束,采用了后台线程的模式。那在这个finalizer中,其运行如下所示:

while (true) {
      try {
        if (!cleanUp(queue.remove())) {
          break;
        }
      } catch (InterruptedException e) { /* ignore */ }
    }

即不断地获取队列中的数据,然后调用cleanUp方法.而在cleanUp方法中,其实就是调用相应对象的回调方法,即当一个对象已经被gc时的回调。

在guava中,从队列中移除的是一个是reference对象。在java体系中,并没有在reference对象中定义相应的回调方法,因此guava为jdk的reference增加了新的定义接口,称之为FinalizableReference。在这个接口中,即定义了一个相应的回调函数,如下所示:

/** 当引用被gc之后,此方法会被触发调用 */
  void finalizeReferent();

因为增加了新的接口,因此我们自己来使用的话,就即要继承于jdk的weakReference,又要实现此接口。为方便,guava定义了3套与jdk兼容的引用对象。即FinalizableWeakReference,FinalizableSoftReference和FinalizablePhantomReference。可以理解为就是一个对jdk原类型的一个适配。

继续阅读“guava中的FinalizableReferenceQueue解析”

ReferenceQueue的使用

1 何为ReferenceQueue

在java的引用体系中,存在着强引用,软引用,虚引用,幽灵引用,这4种引用类型。在正常的使用过程中,我们定义的类型都是强引用的,这种引用类型在回收中,只有当其它对象没有对这个对象的引用时,才会被GC回收掉。简单来说,对于以下定义:

Object obj = new Object();
Ref ref = new Ref(obj);

在这种情况下,如果ref没有被GC,那么obj这个对象肯定不会GC的。因为ref引用到了obj。如果obj是一个大对象呢,多个这种对象的话,应用肯定一会就挂掉了。

那么,如果我们希望在这个体系中,如果obj没有被其它对象引用,只是在这个Ref中存在引用时,就把obj对象gc掉。这时候就可以使用这里提到的Reference对象了。

我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列了。ReferenceQueue即这样的一个对象,当一个obj被gc掉之后,其相应的包装类,即ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。

2 使用队列进行数据监控

一个简单的例子,通过往map中放入10000个对象,每个对象大小为1M字节数组。使用引用队列监控被放入的key的回收情况。代码如下所示:

继续阅读“ReferenceQueue的使用”

Spring中循环引用的处理-3

上文中对调用点A和调用点B的调用方法进行了陈述。接下来,针对创建bean的不同顺序对调用点和调用方法进行分析。

在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定Bean的引用

  singletonFactories earlySingletonObjects singletonObjects
getSingleton(beanName, true)
doCreateBean(beanName,mdb,args)
getSingleton(beanName, true);
addSingleton(beanName, singletonObject)

但是出现循环引用之后呢,就会出现这种情况:

  singletonFactories earlySingletonObjects singletonObjects
getSingleton(A, true); A无B无 A无B无 A无B无
doCreateBean(A,mdb,args) A有B无 A无B无 A无B无
       
populateBean(A, mbd, instanceWrapper) 解析B……  
getSingleton(B, true) A有B无 A无B无 A无B无
doCreateBean(B,mdb,args) A有B有 A无B无 A无B无
populateBean(B, mbd, instanceWrapper)由B准备解析A……  
getSingleton(A, true) A无B有 A有B无 A无B无
完成populateBean(B, mbd, instanceWrapper)解析……  
addSingleton(B, singletonObject) A无B无 A有B无 A无B有
完成populateBean(A, mbd, instanceWrapper)  
       
A- = initializeBean(beanName, exposedObject, mbd)在initializeBean之后A变为A-  
getSingleton(A, false);验证  
addSingleton(A, singletonObject)    ……  

在上面这个过程中,在对A进行验证时,就会从earlySingletonObjects中取得一个A,但是这个A和后面的A-可能不是同一个对象,这是因为有了beanPostProcessor存在,它可以改变bean的最终值,比如对原始bean进行封装,代理等。在这个过程中,出现了3个对象A,A-,B,而B中所持有的A对象为原始的A。如果这里的A和A-不是同一个对象,即产生了beanA有了beanB的引用,但beanB并没有beanA的引用,而是另一个beanA的引用。这肯定不满足条件。

继续阅读“Spring中循环引用的处理-3”

Spring中循环引用的处理-2

上文中对涉及到循环引用的3个方法作了陈述。

在方法1中,对象信息对beanFactory的形式被放入singletonFactories中,这时earlySingletonObjects中肯定没有此对象(因为remove)。

在方法2中,在一定条件下(allowEarlyReference为true)的条件下,对象从singleFactories中的objectFactory中被取出来,同时remove掉,被放入earlySingletonObjects中。这时,earlySingletonObjects就持有对象信息了;当然,如果allowEarlyReference为false的情况下,且earlySingletonObjects本身就没有持有对象的情况下,肯定不会将对象从objectFactory中取出来的。这个很重要,因为后面将根据此信息进行循环引用处理。

在方法3中,对象被加入到singletonObjects中,同时singletonFactories和earlySingletonObjects中都remove掉持有的对象(不管持有与否),这就表示在之前的处理中,这只相当于一个临时容器,处理完毕之后都会remove掉。

继续阅读“Spring中循环引用的处理-2”

Spring中循环引用的处理-1

在使用spring的场景中,有时会碰到如下的一种情况,即bean之间的循环引用。即两个bean之间互相进行引用的情况。这时,在spring xml配置文件中,就会出现如下的配置:

<bean id="beanA" class="BeanA" p:beanB-ref="beaB"/>
<bean id="beanB" class="BeanB" p:beanA-ref="beaA"/>

并且,在一般情况下,这个配置在现有的spring3.0中是可以正常工作的,前提是没有对beanA和beanB进行增强。但是,如果任意一方进行了增强,比如通过spring的代理对beanA进行了增强,即实际返回的对象和原始对象不一致的情况,在这种情况下,就会报如下一个错误:

"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."

这个错误即对于一个bean,其所引用的对象并不是由spring容器最终生成的对象,而只是一个原始对象,而spring不允许这种情况出现,即持有过程中间对象。那么,这个错误是如何产生的,以及在spring内部,是如何来检测这种情况的呢。这就得从spring如何创建一个对象,以及如何处理bean间引用,以及spring使用何种策略处理循环引用问题说起。

继续阅读“Spring中循环引用的处理-1”