对象池模式

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

    }

}