对象池的优点: 复用池中对象, 没有分配内存和创建堆中对象的开销, 没有释放内存和销毁堆中对象的开销, 进而减少垃圾收集器的负担, 避免内存抖动; 不必重复初始化对象状态, 对于比较耗时的constructor和finalize来说非常合适;


对象池的缺点:
(1)现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;
(2)并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;
(3)由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;
(4)很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度);
(5)设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题;


对象池有其特定的适用场景:
(1)受限的, 不需要可伸缩性的环境(比如移动设备): cpu性能不够强劲, 内存比较紧张, 垃圾收集, 内存抖动会造成比较大的影响, 需要提高内存管理效率, 响应性比吞吐量更为重要;
(2)数量受限的资源, 比如数据库连接; (自己写比较容易埋坑, 建议使用成熟的库方案, 比如c3p0)
(3)创建成本高昂的对象, 可斟酌是否池化, 比较常见的有线程池, 字节数组池等; (如果有, 则建议使用成熟的库方案, 比如jdk自带的ThreadPoolExecutor, 而不是自己写)


android在支持库中提供了池的构件单元–Pools, 其中Pools.SimplePool实现比较简单, 采用固定长度的数组和后进先出的策略, 池中对象的内存不会再被释放, 直到池本身被垃圾回收; Pools.SynchronizedPool仅仅是在Pools.SimplePool基础上加了个同步锁;
当然也可以使用apache commons pool2类库作为池的构件单元;

注:
响应性是指一个任务单元需要”多快”能处理完;
吞吐量是指一定的计算资源(如CPU, 内存, 硬盘容量, IO带宽)和单位时间内, 能完成”多少”任务;
可伸缩性是指增加计算资源(如CPU, 内存, 硬盘容量, IO带宽), 程序的吞吐量的能得到多大程度的增加;
Example1:
并行GC缩短了每次stop-the-world的时间来提供响应性, 但做完一次full-GC的任务量要比串行GC耗费更长的时间(吞吐量降低);
Example2:
程序分为彼此独立的表现层/业务逻辑层/持久化层会提供可伸缩性, 但由于在不同系统间传递数据, 比对应的未使用分层模型的程序要慢;