对象池是一种设计模式,它会预先初始化一组可重用的实体,而不是按需销毁然后重建。在使用套接字描述符时,人们通常会将其池化。实际上,套接字描述符的数量通常比较少(最多上千个),之所以要采用池的方式,是因为它们的初始化成本非常高。而在最近发表的一篇博文中,ClojureWerkz核心成员Alex Petrov探讨了另一种对象池应用场景,即将大量的存活期短且初始化成本低的对象池化,以降低内存分配和再分配成本,避免内存碎片。

\\

Alex将对象池看作是减少GC压力的首选方法,同时也是最简单的方法。在下面两种分配模式下,可以选择使用对象池:

\\

  • 对象以固定的速度不断地分配,垃圾收集时间逐步增加,内存使用率随之增大;\\t
  • 对象分配存在爆发期,而每次爆发都会导致系统迟滞,并伴有明显的GC中断。\

在绝大多数情况下,这些对象要么是数据容器,要么是数据封装器,其作用是在应用程序和内部消息总线、通信层或某些API之间充当一个信封。这很常见。例如,数据库驱动会针对每个请求和响应创建Request和Response对象,消息系统会使用Message和Event封装器,等等。对象池可以帮助保存和重用这些构造好的对象实例。

\\

Alex介绍了两种基本的对象池回收模式:“借用(borrowing)”和引用计数。前者更清晰,而后者则意味着要实现自动回收。

\\

借用非常像垃圾收集运行时之上的malloc/free。自然地,在使用这种方式时,开发人员需要面对早先使用非垃圾收集语言时面对的问题。如果某个对象已经释放并返回到池中,那么任何对它的修改或读取都会产生不可预见的结果。例如,在C语言中,对已释放的指针进行任何操作都会产生块错误。借用适用于有明确的开始/结束点的操作。绝大多数时候,都不要将它用于对象可以被多个线程同步访问的情况。借用最大的优点是,它不知道对象池的存在。被借用的对象本身要有某种reset机制,借用和返回操作都由对象消费者完成。

\\

引用计数在实现方面稍微复杂些,但它对数据结构提供了更细粒度的控制。将对象池封装到一个函数式接口中,消费者就可以不必了解它,就像下面这个样子:

\\

\(pooledObject, pooledObjectConsumer) -\u0026gt; { pooledObject.retain(); pooledObjectConsumer.accept(pooledObject); pooledObject.release(); }; 

\\

每当对象进入上述代码块,调用者就会retain该对象,并在执行块执行完毕后将其release。每个对象都持有一个内部计数器和一个指向池的引用。当计数器为0时,对象就会返回池中。

\\

通常,引用计数用于同时有多个消费者访问已分配对象的情况,只有当所有的消费者都释放了对象引用时,对象才可以被回收。这种方式也适用于管道或嵌套处理。在这种情况下,开发者可以避免显式的开始/结束操作。

\\

分配触发负责在池中对象不足时分配新资源。Alex介绍了如下三种分配触发方式:

\\

  • 空池触发:任何时候,只要池空了,就分配对象。这是一种最简单的方式。\\t
  • 水位线:空池触发的缺点是,某次对象请求会因为执行对象分配而中断。为了避免这种情况,可以使用水位线触发。当从池中请求新对象时,检查池中可用对象的数量。如果可用对象小于某个阈值,就触发分配过程。\\t
  • Lease/Return速度:大多数时候,水位线触发已经足够,但有时候可能会需要更高的精度。在这种情况下,可以使用leasereturn速度。例如,如果池中有100个对象,每秒有20个对象被取走,但只有10个对象返回,那么9秒后池就空了。开发者可以使用这种信息,提前做好对象分配计划。\

增长策略用于指定分配过程被触发后需要分配的对象的数量。Alex也介绍了三种方式:

\\

  • 固定大小:这是最简单的对象池实现方式。对象一次性预分配,对象池后续不再增长。这种实现适用于对象数量相对确定的情况,但池大小固定可能会导致资源饥饿。\\t
  • 小步增长:为了避免出现资源饥饿,可以允许对象池小步增长,比如一次额外分配一个对象。\\t
  • 块增长:如果无法接受分配导致的中断,就需要保证池中任何时候都有可用的对象。这时,就必须使用块增长。例如,每当水位线到达25%时,就将对象池增大25%。不过,这种方式容易导致内存溢出。搭配Lease/Return速度分配触发策略,可以得出更准确的池大小。\

当然,使用对象池就意味着开发者开始自己管理内存,所以需要注意以下问题:

\\

  • 引用泄露:对象在系统中某个地方注册了,但没有返回到池中。\\t
  • 过早回收:消费者已经决定将对象返还给对象池,但仍然持有它的引用,并试图执行写或读操作,这时会出现这种情况。\\t
  • 隐式回收:当使用引用计数时可能会出现这种情况。\\t
  • 大小错误:这种情况在使用字节缓冲区和数组时非常常见:对象应该有不同的大小,而且是以定制的方式构造,但返回对象池后却作为通用对象重用。\\t
  • 重复下单:这是引用泄露的一个变种,存在多路复用时特别容易发生:一个对象被分配到多个地方,但其中一个地方释放了该对象。\\t
  • 就地修改:对象不可变是最好的,但如果不具备那样做的条件,就可能在读取对象内容时遇到内容被修改的问题。\\t
  • 缩小对象池:当池中有大量的未使用对象时,要缩小对象池。\\t
  • 对象重新初始化:确保每次从池中取得的对象不含有上次使用时留下的脏字段。\

最后,Alex指出:

\\

\

对象池并不适合所有人。在应用程序开发的早期阶段就开始使用对象池是没有意义的,因为你那时候还不能确切地知道什么需要池化,也不确定如何池化。

\

\\


感谢郭蕾对本文的审校。

\\

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号:InfoQChina)关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入InfoQ读者交流群)。

一个广为人知但鲜有人用的技巧:对象池相关推荐

  1. 抓到Netty一个隐藏很深的内存泄露Bug | 详解Recycler对象池的精妙设计与实现

    本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的时候,不小心用我的肉眼抓到了一个隐藏很深很深的内存泄露 Bug. 于是笔者将这个故事-哦不 -事 ...

  2. [摘录]第8章 与非美国人谈判的技巧

    [摘录]第8章 与非美国人谈判的技巧 第四部分 知己知彼 互利共赢 可事实上,电影和电视节目并没有深刻揭示出一个真实的美国,而要想了解美国人的商业方式,就必须了解美国人的真实心态和思维方式.反过来说, ...

  3. excel 两组数据交点_让科研人相见恨晚的技巧,用Excel做柱状图、箱型图及数据分析!(转载自ZSCI)...

    来源:ZSCI 让科研人相见恨晚的技巧,用Excel做柱状图.箱型图及数据分析! 面对大量的实验数据,却不知道如何快速的将自己想要的结果筛选出来.筛选后也只能做简单的数据图,绘制高级的图形又成了一个难 ...

  4. 如何打造领英朋友圈_有哪些领英快速扩充人脉的技巧?

    为什么在LinkedIn领英上搜到的客户都是显示领英会员(Linkedin Member)?无法向对方发送添加好友邀请?也无法向访问对方的LinkedIn领英个人主页?这是很多人都遇到的难题,也是使用 ...

  5. 2010-2019,我,一个普通职场人的十年变迁

    一位朋友在知乎分享了他的十年变迁. 他毕业于欧美名校,回国十年赶上移动互联网的浪潮,先后服务于几家顶级互联网公司,起起落落,感受到了互联网带来的荣耀和辉煌. 可以说他是这个时代的弄潮儿. 我很羡慕. ...

  6. 你的团队需要一个会讲故事的人

    还有一个小时才开始电影,和朋友随便逛了逛,想到这么大的商场应该会有书店吧! 果不其然,在网上查了下,地下负一楼新开了一个书店--西西弗书店.                  每到一个地方,总是喜欢去 ...

  7. 整理:学术论文发表过程中的审稿人意见答复技巧

    整理:学术论文发表过程中的审稿人意见答复技巧 更新历史 20200613: 首次发布 科技论文投稿后,通常都需要答复审稿人的意见.对于审稿人的意见,不能随便敷衍,因为这会让审稿人感到不高兴,说不定转头 ...

  8. 产品读书《你的团队需要一个会讲故事的人》

    思维导图 作者简介 安妮特·西蒙斯,美国某顾问公司的总裁,给美国国家航空航天局.美国国税局.微软公司做过培训.还有一个身份是故事教练,教企业学会讲故事,曾经在美国全国广播公司.美国国家公共电台做过系列 ...

  9. 你的团队需要一个会讲故事的人读书笔记

    本书是一本讲述理论的图书,看完后最大的感受是当初应该尽量的购买原本读物.这本书宣讲的主题我认为非常好,第一次读这类主题的书,再结合上当下工作环境,感觉本书给了我一定的启发,整体下来这书值得一看,当然如 ...

最新文章

  1. Python工具 | 9个用来爬取网络站点的 Python 库
  2. linux路由器转发效率,如何使用Intel 10 Gbe解决Linux路由器/防火墙转发性能问题?...
  3. contentProvider的使用总结
  4. 资源 | 来自独秀同学的深度网络数学笔记,还不快收藏?
  5. icon-font与svg
  6. python绘制多个条形图_python – 在Matplotlib中绘制多个直方图 – 颜色或并排条形图...
  7. MySQL安装时MySQL server一直安装失败日志显示This application requires Visual Studio 2013 Redistributable...
  8. C++newton raphson method牛顿拉夫森法的实现算法(附完整源码)
  9. 迅速步入jQuery的殿堂
  10. Python 笔记(一)字典与json使用及注意点
  11. 强制卸载Lync Server脱机的CMS并在新位置重建
  12. 笔试题--计算组合数
  13. 系统学习NLP(十七)--文本相似度
  14. 登陆csdn卡死机,进入不了csdn内容管理页面终极解决方案
  15. 低通滤波器转带通滤波器公式由来_什么是-3dB截止频率?浅析滤波器原理、分类和滤波器优化!...
  16. 2021年物联网设备CVE天梯榜
  17. java 基础运算_Java 基础 运算符
  18. java开发环境(java开发环境和运行环境)
  19. centos7 配置ssh
  20. 微信小程序与VS webapi局域网内联调

热门文章

  1. 使用Vux WechatEmotion组件引发的一系列血案
  2. 【Python】supervisor 工具介绍
  3. openstack windows下远程debug调试(komodo)
  4. 老是说我编译版本不够_编译etcd出现的cannot load bufio的错误解决办法
  5. Linux下一些简单命令的收集
  6. 成功的MES项目,前期都做了些什么?
  7. 动态规划问题以及诸多实例分析
  8. Linux/AIX上部署VNC Server
  9. 常用端口号及对应服务
  10. openerp one2many 字段排序