WebKit for Android分析: ByteArrayBuilder

ByteArrayBuilder并不是WebKit for Android代码中的一个重要类,其代码也不长,只有154行。但是其中使用到的Java特性却是我第一次碰到。《Java编程思想》这本书我也看过两遍,但读了ByteArrayBuilder代码,才知道Java中有SoftReference,为此恶补了一下Java基础知识。要读懂ByteArrayBuilder代码,需要弄懂Java中的引用概念。

一、Java中的强引用、弱引用、软引用和虚引用

与引用相关的类位于java.lang.ref包,分别为Reference, WeakReference, SoftReference, PhantomReference,另外一个与之相关的类为ReferenceQueue。类关系图为:

 JavaReferenceClassDiagram

1)强引用(Reference)

强引用是使用最普遍的引用,当我们new一个对象时,就得到对象的强引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemory错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

2)软引用(SoftReference)

如果一个对象只具有软引用,则只要内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。由于软引用只在内存不足的情况下被回收,可用来实现缓存池。

3)弱引用(WeakReference)

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

4)虚引用(PhantomReference)

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同 ,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用 ,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

还有一个重要的类就是ReferenceQueue这个类,软引用/弱引用/虚引用都可以关联一个ReferenceQueue(虚引用必须和引用队列联合使用),如果软/弱/虚引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软/弱/虚引用加入到与之关联的引用队列中。

程序可以通过判断引用队列中是否已经加入了弱/软/虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个弱/软/虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。


二、为什么需要使用软引用

ByteArrayBuilder类中只用到软引用,所以这里重点讨论一下软引用。

软引用的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。 也就是说,一旦软引用保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。

看下面代码:

MyObject aRef = new MyObject();
SoftReference aSoftRef = new SoftReference (aRef);

此时,对于这个MyObject对象,有两个引用路径,一个是来自软引用对象的软引用,一个来自变量aRef的强引用 ,所以这个MyObject对象是强可及对象。

随即,我们可以结束aRef对这个MyObject实例的强引用:

aRef = null;

此后,这个MyObject对象成为了软可及对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个软引用对该对象的引用而始终保留该对象。Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象, 而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。在回收这些对象之前,我们可以通过:

MyObject anotherRef=(MyObject)aSoftRef.get ();

重新获得对该实例的强引用。而回收之后,调用get()方法就只能得到null了。

回想如果在C++实现缓存池,需要定义缓存池的大小、缓存老化机制等,如果使用软引用来实现缓存池就简单多了,无需定义缓存池的大小,Java虚拟机也提供了算法,优先回收长时间闲置不用的软可及对象。

三、ByteArrayBuilder代码分析

有了对软引用的认识后,ByteArrayBuilder的代码就不难理解了,其主要目的是减少小块数据的分配,类似于在C程序中为了减少内存碎片而建立的内存池分配器。

ByteArrayBuilder有一个内部类Chunk,用来定义数据块,其成员变量mArray是为数据块分配的大小,成员变量mLength记录实际数组使用的大小。成员函数Release释放数据块,将软引用加入缓冲池链表中,供下次重复利用。

ByteArrayBuilder的一个重要成员方法是append,其作用就是将数组数据添加到数据块链表中,其流程就是先判断缓冲池链表中是否存在数据块,然后复制数据到数据块中。如果数据块不够大,需要再获取数据块,直到数据都复制到数据块链表中。

如果要获取ByteArrayBuilder中的数据,需要循环调用getFirstChunk成员方法,直到返回null。

总体看ByteArrayBuilder,主要减少了内存分配与回收,但也付出了代价,锁和数据拼接。所以如果仅仅是单个的数组,使用本类是不恰当的,它只适合拼接小块的数据,比如从网络上接收的数据块。

参考:

1. Java中对象的强、软、弱和虚引用

2. Android SDK Docs(https://mogoweb.net/133-2/ )

WebKit for Android分析: ByteArrayBuilder》上有2条评论

    1. alex 文章作者

      非常感谢。个人觉得加上水印没什么不好,如果是真心觉得vp好,花钱购买,利人利己。花时间折腾,还不如把时间节省下来,做更有意义的事情。人工成本其实也很高的。

      回复

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>