_StriveG Blog

了解对象池

认识对象池

对象池是一种设计模式,预先初始化一组可重用的实体,而不是按需销毁后重建。主要是为了解决创建对象以及gc造成的性能损耗。适用于以下情景:

  • 创建对象消耗大量的资源,如线程,jdbc连接,socket连接
  • 创建小对象但是gc频繁,如内存抖动现象,如Android中的Message

对象池的设计中,涉及到以下几个方面:

  • 对象池容量
  • 从对象池中取对象
  • 向对象池中存对象
  • 高并发对象池

因为本人是做客户端开发的,所以以Android中几种不同的对象池实现方式来了解下线程池。

Parcel类的对象池设计

我们现在主要看这个类对对象池的设计。以obtain方法为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
return p;
}
}
}
return new Parcel(0);
}

在这里,我们要注意到这里的池 是一个数组,从里提对象的时候,从头到尾扫描,找到不为null的对象,返回。再来看下recycle方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final void recycle() {
if (DEBUG_RECYCLE) mStack = null;
freeBuffer();
final Parcel[] pool;
if (mOwnsNativeParcelObject) {
pool = sOwnedPool;
} else {
mNativePtr = 0;
pool = sHolderPool;
}
synchronized (pool) {
for (int i=0; i<POOL_SIZE; i++) {
if (pool[i] == null) {
pool[i] = this;
return;
}
}
}
}
  • 找数组中第一个为null的,插入进入。

当然,上面的代码还存在可优化的地方,原因在对存取的时候时复杂度都是O(n),那么下面我们就来看一种,稍微优化过的。

v4包中的Pools类

同样,这里还是用数组来实现对象池。

1
2
3
4
5
6
7
8
9
10
public T acquire() {
if (mPoolSize > 0) {
final int lastPooledIndex = mPoolSize - 1;
T instance = (T) mPool[lastPooledIndex];
mPool[lastPooledIndex] = null;
mPoolSize--;
return instance;
}
return null;
}

能看到优化么?这里和原先的不一样,我们现在不便利了,直接从后面取,取一个,size–、这样,时间复杂度就是0(1)了,确实做到了优化,当然,release方法也一样。

1
2
3
4
5
6
7
8
9
10
11
public boolean release(T instance) {
if (isInPool(instance)) {
throw new IllegalStateException("Already in the pool!");
}
if (mPoolSize < mPool.length) {
mPool[mPoolSize] = instance;
mPoolSize++;
return true;
}
return false;
}

除了使用数组的方式,Android代码中还有以单链表来实现对象池的类,就是俺们的Message。

Message-单链表的对象池设计

我们看下他的取和存(回收)。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
  • 取对象的时候,我们从链表头拿个对象
  • 回收的时候,相当于在头部插入一个节点。

其他的一些设计

其他的一些设计 如线程池,socket连接池这里就不说了。

参考资料

最近访客