对象池模式
Object Pool 对象池
看这个词,一般都能联想到线程池,线程池所使用的模式就是对象池模式
通过回收利用对象避免获取和释放资源所需要的大量开销
此处的回收并不是 GC 回收,而是手动维护一个“池”,放的就是这些不再使用的对象,等待需要的时候从池中取出;
线程的创建和销毁开销非常大,如果频繁的创建线程,这些开销就会被方法,因此用线程池来维护指定数量的线程,避免线程的频繁创建;
除了线程池,在 Android 中对象池模式也比较容易见到:
Handler 机制中的 Message
触摸事件的 MotionEvent
Bitmap
...
这些对象或者需要频繁创建、或者创建时需要分配较大的内存,如果不池化,极容易出现内存抖动(多次GC),导致UI线程卡顿;
因此它们内部都维护了对应的对象池,也提供了相对应的方法来获取和回收对象;
什么时候可以使用?
对象可以被重用。
对象创建开销较大(如Bitmap)
对象创建频率较高(如 Message、MotionEvent)
对象被使用的时间较短(比如 Message 加入消息队列后很快就会被处理,MotionEvent 被分发的过程很短)。
对象池的优点:
节省创建类的实例开销和时间
避免所占堆内存增大,导致内存抖动;
例子
可以模仿 Message 这些来实现对象池模式
首先需要思考用什么数据结构来维护这些回收的对象呢?
需要池化的对象一般都会频繁获取和回收,那么这个数据结构就需要支持快速的插入和删除;
最合适的就是 单链表 了
对象池应该是全局唯一,对象的回收应该都到该对象池中,因此使用 单例模式 来实现这个对象池
假设现在有一个 Task 类会被频繁创建,声明如下:
class Task {
var name: String? = null
var `when`: Long? = null
var action: (() -> Unit)? = null
}所提供的获取和回收方法实际上是对单链表的移除和删除:
考虑到线程安全,还需要在单链表操作时进行加锁;
class Task {
var name: String? = null
var `when`: Long? = null
var action: (() -> Unit)? = null
// 单链表结构字段
private var mNext: Task? = null
companion object {
private var mPool: Task? = null
private var mPoolSize: Int = 0
private val mPoolRecycleLock = Any()
}
/** 回收对象 */
fun recycle() {
// 需要清空自身内容
name = null
`when` = null
action = null
// 插入单链表
synchronized(mPoolRecycleLock) {
mNext = mPool
mPool = this
mPoolSize++
}
}
/** 获取对象 */
fun obtain(): Task {
synchronized(mPoolRecycleLock) {
if (mPool != null) {
val recycle = mPool!!
mPool = mPool!!.mNext
mPoolSize--
return recycle
} else {
return Task()
}
}
}
fun obtain(task: Task): Task {
val newInstance = obtain()
newInstance.action = task.action
newInstance.name = task.name
newInstance.`when` = task.`when`
return newInstance
}
}使用时,使用 Task.obtain() 方法来获取该对象,不再使用时使用 Task.recycle() 来回收该对象
当对象不断被回收,而没有被获取的时候,此时单链表会变得越来越长,所占内存也会变得越来越大
因此可以限制对象池会回收的对象数量;
companion object {
private var mPool: Task? = null
private var mPoolSize: Int = 0
private val mPoolRecycleLock = Any()
// 限制回收对象数目
private const val maxPoolSize: Int = 50
}
// 插入单链表
synchronized(mPoolRecycleLock) {
if (mPoolSize < maxPoolSize) {
mNext = mPool
mPool = this
mPoolSize++
}
}同时可以进行强制规范使用,即调用了 recycle 之后对象处于回收状态,不能再使用,只有调用了 obtain 才能进行使用;
这样同时也能避免同一个对象重复进入对象池(对象池如果存在多个相同的引用,在获取之后可能获取到正在使用的对象)
使用标志位或着boolean来判断
class Task {
private var mFlags = 0 // 标志位判断
companion object {
private const val FLAG_IN_RECYCLE = 1 shl 1 // recycle 标志位
}
// 标记处于已经回收
private fun markInRecycle() {
mFlags = mFlags or FLAG_IN_RECYCLE
}
// 检查是否处于回收
private fun checkInRecycle(): Boolean {
return (mFlags and FLAG_IN_RECYCLE) != 0
}
/** 回收对象 */
fun recycle() {
// 检查
if (checkInRecycle()) {
throw IllegalStateException("recycle again")
}
// 需要清空自身内容
name = null
when = null
action = null
// 标记上
markInRecycle()
// 插入单链表
synchronized(mPoolRecycleLock) {
if (mPoolSize < maxPoolSize) {
mNext = mPool
mPool = this
mPoolSize++
}
}
}
}完整代码
class Task {
var name: String? = null
var when: Long? = null
var action: (() -> Unit)? = null
// 单链表结构字段
private var mNext: Task? = null
// 标志位
private var mFlags = 0
companion object {
// 对象池,单链表结构
private var mPool: Task? = null
// 当前对象池大小
private var mPoolSize: Int = 0
// 同步锁
private val mPoolRecycleLock = Any()
// 最大回收数量
private const val maxPoolSize: Int = 50
// 回收标志位
private const val FLAG_IN_RECYCLE = 1 shl 1 // recycle 标志位
}
private fun markInRecycle() {
mFlags = mFlags or FLAG_IN_RECYCLE
}
private fun checkInRecycle(): Boolean {
return (mFlags and FLAG_IN_RECYCLE) != 0
}
/** 回收对象 */
fun recycle() {
if (checkInRecycle()) {
throw IllegalStateException("recycle again")
}
// 需要清空自身内容
name = null
when = null
action = null
markInRecycle()
// 插入单链表
synchronized(mPoolRecycleLock) {
if (mPoolSize < maxPoolSize) {
mNext = mPool
mPool = this
mPoolSize++
}
}
}
/** 获取对象 */
fun obtain(): Task {
synchronized(mPoolRecycleLock) {
if (mPool != null) {
val recycle = mPool!!
mPool = mPool!!.mNext
mPoolSize--
return recycle
} else {
return Task()
}
}
}
fun obtain(task: Task): Task {
val newInstance = obtain()
newInstance.action = task.action
newInstance.name = task.name
newInstance.`when` = task.`when`
return newInstance
}
}