java 对象池 博客_Android中的Message类以及Java对象池的实现

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

大意是说,虽然Message类的构造方法是public的,你可以直接通过new来创建一个新的对象,但是最好还是通过Message.obtain()或者Handler.obtainMessage()来创建一个新的Message对象,因为这两个方法会重用之前创建的但是已经不再使用了的对象实例。

这句话不禁引起了我的兴趣,因为之前我写过一篇博客《Pool, SimplePool与SynchronizedPool》,在这篇博客里面仔细分析了Android是如何实现一个对象池的。里面提到VelocityTracker就是用SynchronizedPool来实现对象重用的,代码如下:VelocityTracker tracker = VelocityTracker.obtain();

tracker.recycle();

Message类看起来也略有相似,不过经过阅读Message类的源代码,发现我错了,Message类使用了另一种巧妙的方法来实现对象重用。

好了,不卖关子了,Message类使用了一个链表来实现对象池,而且是一个前端链表,即在前端插入和删除的链表,避免了插入和删除的时候遍历整个链表。是不是有点出人意料?

首先看一下这段代码,去除了Message中其他的携带消息信息的字段。已经很明显可以看出来是一个链表了吧。public final class Message implements Parcelable {

// 省略其他代码

Message next;                                          // (1)

private static final Object sPoolSync = new Object();  // (2)

private static Message sPool;                          // (3)

private static int sPoolSize = 0;                      // (4)

private static final int MAX_POOL_SIZE = 50;

// 省略其他代码

}

(1) 声明了next指针

(2) 对象锁,Message对象池是线程安全的,这样就需要在向对象池申请和归还对象时使用锁

(3) 这里是关键,一个静态的Message对象,这就是这个链表的头指针了,因为是类变量,因此,整个JVM中只有一个

(4) 当前对象池的大小,后面还限制了这个Message对象池中的对象个数最大为50

用图形表示如下:

f6fb886f0db2ea8490d162c72550b8bb.png

继续阅读代码,又发现了一个让人困惑的问题,看这个方法:public static Message obtain() {

synchronized (sPoolSync) {      // (1)

if (sPool != null) {

Message m = sPool;      // (2)

sPool = m.next;         // (3)

m.next = null;          // (4)

sPoolSize--;            // (5)

return m;

}

}

return new Message();

}

这也很容易理解:

(1) 获取锁,这里毫无疑义

(2) 当头指针指向的对象不为null时,将这个对象赋值给m

(3) 将头指针指向m的next指针

(4) 将m的next指向null,到这里位置,我们从以sPool为头指针的链表中取出了第一个元素

(5) 将链表size减1

看起来没错,疑问是,当第一次获取对象的时候sPool肯定为null,那么这个if语句肯定不会执行,会直接执行最后一句return new Message(),直接创建一个对象?这样不是毫无意义了么?

看到这里,似乎感觉发现了一个Android的bug,会有这么明显的bug么?当然不会,静下心来继续读代码,读到这里的时候豁然开朗了:public void recycle() {

clearForRecycle();                        // (1)

synchronized (sPoolSync) {

if (sPoolSize 

next = sPool;                     // (3)

sPool = this;                     // (4)

sPoolSize++;

}

}

}

(1) 重置Message中携带的消息

(2) 检查当前对象池的大小是不是已经超过了最大值,如果当前队列已经满了,就不管这个对象了,让JVM的GC回收好了,这里保证了性能的同时兼顾了内存消耗

(3) 将当前对象next指针指向头指针sPool指向的对象

(4) 将头指针sPool指向当前对象,然后将对象池大小加1

到这里就明白了:这篇博客开头的那句话其实背后还有一些潜台词,那就是你必须显式的调用一下recycle()将当前的Message对象归还到对象池,这个对象池才能发挥其效果,不调用recycle()方法,对象不会归还,会被JVM GC回收。

也就是说下面两句话必须是成对出现的,不用obtain()而调用recycle()会导致不停的创建Message对象直到超过MAX_POOL_SIZE的限制而被对象池扔掉;通过obtain()申请对象而不用recycle()归还会导致对象池被消耗干净而不停申请新对象。Message msg = Message.obtain();

msg.recycle();

所以文档再完整还是不如看代码。

对于通过SynchronizedPool来实现对象池和这种通过链表来实现对象池两种方法,我看不出来各自有何优缺点,这两种方法很相似,实现的功能也相似,唯一的不同在于,前者似乎更容易扩展。也许你自己在其他项目中需要对象池的时候,可以借鉴一下这两种方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值