从一段奇怪代码开始说

1,510 阅读4分钟
原文链接: blog.csdn.net
看Fresco的代码中,有这样的一个类:

/**

当看到类的名字的时候就感觉奇怪,因为大家听到OOM都感觉害怕,为什么还起这样的一个名字?
带着疑问,看了下代码,三个SoftReference引用同一个对象,甚是出奇。WHY?
然后看注释,注释一大段,其实就只有一个关键点:什么是SoftReference?有何特点?

SoftReference

在android developer官方文档上对SoftReference是这样描述的:

Soft reference objects, which are cleared at the discretion of the garbage collector in response to memory demand.

Suppose that the garbage collector determines at a certain point in time that an object is softly reachable. At that time it may choose to clear atomically all soft references to that object and all soft references to any other softly-reachable objects from which that object is reachable through a chain of strong references. At the same time or at some later time it will enqueue those newly-cleared soft references that are registered with reference queues.

All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError . Otherwise no constraints are placed upon the time at which a soft reference will be cleared or the order in which a set of such references to different objects will be cleared. Virtual machine implementations are, however, encouraged to bias against clearing recently-created or recently-used soft references.


注意红色段落的描述:所有的 SoftReference必定会在OOM发生前被回收 ,但是,究竟虚拟机要回收哪个SoftReference或者回收的顺序是怎么样是没有限制的。虚拟机的实现一般倾向于清掉最新创建或者最新被使用的SoftReference。

看到这里,有一个想法就是,三个SoftReference的作用应该和GC对SoftReference的回收策略有关 ,至于有何相关依然很难确定。恰巧,上面的代码注释中,有两个链接: https://goo.gl/Pe6aS7 https://goo.gl/BYaUZE链接到的页面就是dalvik和art虚拟机的源代码,这就印证了我们的想法。那么我们就看看虚拟机对SoftReference的回收策略是如何的?

Virtual Machine GC

循着注释的链接,我们去看一下虚拟机GC对SoftReference的回收策略。选择dalvik虚拟部分去看:
/*
MarkSweep.cpp

总所周知,dalvik虚拟机的GC算法是Mark-Sweep,即回收过程主要分两部分,Mark 阶段是对对象进行标记,Sweep阶段是对没有被标记的对象进行回收。
大概的分析可以知道上面的函数是主要作用是:遍历所有的SoftReference,位置是偶数位的进行Mark,奇数位的对象进入清除队列

究竟是不是这样,我们看调用该函数的地方:
/*

再看clearWhiteReferences 方法:

/*

从上面的代码可以知道,preserveSomeSoftReferences 函数的作用其实就是保留一部分的SoftReference引用的对象,另外一部分就会被垃圾回收掉 ,而这个策略就是位置的奇偶性

然后我们回到,那段注释
 This means that as long as one of the soft references stays alive, they all stay alive. If we

感觉有点恍然大悟,注释的意思就是说:如果两个SoftReference相邻(一奇一偶),那么这两个 SoftReference引用的对象就不会被GC回收掉,但是,SoftReference的位置是不能够确定的,所以,为了“安全起见”,使用三个SoftReference(为什么不是10个)去引用对象,尽可能地防止被GC回收。

到此,我们基本明白这个OOMSoftReference为什么改这个名字了,目的就是防止对象被GC回收掉,那么,如果这样做不就真的容易引起OOM的发生吗?其实不然。原因就是前面提到的,“所有的 SoftReference必定会在OOM发生前被回收 ”。

GC_BEFORE_OOM

继续追根究底,所有的真相都在代码中,下面这段代码是当需要分配任何一个对象内存时,都会调用的

/* Try as hard as possible to allocate some memory.

看看gcForMalloc 函数:

/* Do a full garbage collection, which may grow the

/*

再看看GC_BEFORE_OOM

static const GcSpec kGcBeforeOomSpec = {


看完上面代码,基本了解了为什么说所有的 SoftReference必定会在OOM发生前被回收 ”。

原因是:当进程不断申请内存,如果一直申请不到(尝试了多次,Heap大小已经不能再增长),那么dalvik虚拟机会触发 GC_BEFORE_OOM 类似的回收方式,触发这种类型GC,会保证所有SoftReference引用的对象,都会被回收掉。

Conclusions

至此,三个SoftReference的谜团终于解开,至于为什么Fresco这样做,个人猜想是,Fresco希望尽量自己管理内存的分配和释放,所以要防止对象被回收掉,避免重新分配内存,起到缓存池的作用。那为什么不使用strong reference,因为在自己管理的同时可以保证在系统内存资源紧张时,能够依赖GC,释放掉SoftReference引用对象的内存,避免真的发生OOM。

对于Art部分的回收机制,这里就不在深入,基本差不多,有兴趣的自行深究。