系统性能调优必知必会
陶辉
智链达 CTO,前阿里云 P8 高级技术专家
36367 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
系统性能调优必知必会
15
15
1.0x
00:00/00:00
登录|注册

02 | 内存池:如何提升内存分配的效率?

性能对比
适用场景
解决方法
子线程预分配
预分配策略
堆 vs 栈
C库内存池选择
内存池影响
速度比较
内存分配方式
TCMalloc
Ptmalloc2
JVM堆内存池
C库内存池
应用层内存池
性能提升
作者:陶辉
思考题
小结
堆 vs 栈
C库内存池分析
隐藏的内存池
介绍
内存池:如何提升内存分配的效率?

该思维导图由 AI 生成,仅供参考

你好,我是陶辉。
上一讲我们提到,高频地命中 CPU 缓存可以提升性能。这一讲我们把关注点从 CPU 转移到内存,看看如何提升内存分配的效率。
或许有同学会认为,我又不写底层框架,内存分配也依赖虚拟机,并不需要应用开发者了解。如果你也这么认为,我们不妨看看这个例子:在 Linux 系统中,用 Xmx 设置 JVM 的最大堆内存为 8GB,但在近百个并发线程下,观察到 Java 进程占用了 14GB 的内存。为什么会这样呢?
这是因为,绝大部分高级语言都是用 C 语言编写的,包括 Java,申请内存必须经过 C 库,而 C 库通过预分配更大的空间作为内存池,来加快后续申请内存的速度。这样,预分配的 6GB 的 C 库内存池就与 JVM 中预分配的 8G 内存池叠加在一起,造成了 Java 进程的内存占用超出了预期。
掌握内存池的特性,既可以避免写程序时内存占用过大,导致服务器性能下降或者进程 OOM(Out Of Memory,内存溢出)被系统杀死,还可以加快内存分配的速度。在系统空闲时申请内存花费不了多少时间,但是对于分布式环境下繁忙的多线程服务,获取内存的时间会上升几十倍。
另一方面,内存池是非常底层的技术,当我们理解它后,可以更换适合应用场景的内存池。在多种编程语言共存的分布式系统中,内存池有很广泛的应用,优化内存池带来的任何微小的性能提升,都将被分布式集群巨大的主机规模放大,从而带来整体上非常可观的收益。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

内存池是提高内存分配效率的重要技术,本文深入介绍了内存池的概念及其在系统中的作用。首先,文章引起读者兴趣,通过Java进程内存占用超出预期的案例,解释了内存池的重要性。接着详细介绍了隐藏的内存池,包括应用层内存池、C库内存池以及操作系统内核内存池的关系和作用。文章还对Linux系统的默认C库内存池Ptmalloc2进行了具体分析,解释了其预分配策略及对性能的影响。此外,文章提出了解决Java进程受到C库内存池影响的两种解决办法,即调整Ptmalloc2的工作方式和更换掉Ptmalloc2内存池。最后,文章提到了Google的TCMalloc和Ptmalloc2是目前最主流的两个内存池,并通过对比TCMalloc与Ptmalloc2内存池,指导读者如何选择内存池。文章还讨论了从堆还是栈上分配内存的问题,以及内存池对初始化对象的帮助。总的来说,本文深入浅出地介绍了内存池的概念及其在系统中的作用,为读者提供了有益的指导,使他们能够更好地了解内存池的原理和选择。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《系统性能调优必知必会》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(54)

  • 最新
  • 精选
  • 忆水寒
    1、思考题:内存池中可以利用享元模式将常用的对象一直保留着,减少重复申请导致的性能的顺耗。 2、最后一段话“内存分配时间虽然不起眼,但时刻用最快的方法申请内存,正是高手与初学者的区别。”说的很好,是的,真正的高手应该能够从算法到底层都能优化。

    作者回复: 忆水寒同学说得对,享元模式这个词用得也很好!享元模式有广泛的应用,不只在应用层,在内核中也被广泛使用。

    2020-04-27
    6
    44
  • ermaot
    解决了我很多疑惑。比如mysql很多人建议把内存分配换成tcmalloc,就是因为mysql要支持大量并发,适合tcmalloc的应用场景。没有对比就没有发现,两库一比,知识点就出来了

    作者回复: 很高兴能帮到你,我跟编辑一直担心这个会不会讲得太深了呢,其实很多性能优先的组件都可以用得上

    2020-04-27
    3
    35
  • aoe
    1. 原来Java堆的内存空间是通过C库内存池申请! 2. 第一次知道内存分配器的存在:Ptmalloc2、TCMalloc 3. 在栈中申请内存比堆中快是因为不需要加锁。 收获惊呆了!

    作者回复: 又添aoe大招了^_^

    2020-04-28
    3
    24
  • 探索无止境
    老师你好,我有一个疑问 TCMalloc既然可以针对小内存做优化,为何不对中等内存和大内存一起做优化?是技术上实现有困难吗?能否从技术实现的角度来聊聊,为何它只优化了小内存的场景

    作者回复: 对小内存做优化,其实是会造成内存利用率下降的,特别是每个线程独立维护内存池,就拒绝线程之间共享内存池了。由于小内存的使用最为频繁,所以利用率的下降表现不明显。但对大内存做上述优化,就会造成利用率下降过多,性价比不划算。

    2020-06-10
    2
    13
  • 万历十五年
    解决ptmalloc2内存过大的三种方案(转自http://fengfu.io): 第一种:控制分配区的总数上限。默认64位系统分配区数为:cpu核数*8,如当前环境16核系统分配区数为128个,每个64M上限的话最多可达8G,限制上限后,后续不够的申请会直接走mmap分配和munmap回收,不会进入ptmalloc2的buffer池。 所以第一种方案调整一下分配池上限个数到4: export MALLOC_ARENA_MAX=4 第二种:之前讲到ptmalloc2默认会动态调整mmap分配阈值,因此对于较大的内存请求也会进入ptmalloc2的内存buffer池里,这里可以去掉ptmalloc的动态调整功能。可以设置 M_TRIM_THRESHOLD,M_MMAP_THRESHOLD,M_TOP_PAD 和 M_MMAP_MAX 中的任意一个。这里可以固定分配阈值为128K,这样超过128K的内存分配请求都不会进入ptmalloc的buffer池而是直接走mmap分配和munmap回收(性能上会有损耗,当前环境大概10%)。: export MALLOC_MMAP_THRESHOLD_=131072 export MALLOC_TRIM_THRESHOLD_=131072 export MALLOC_TOP_PAD_=131072 export MALLOC_MMAP_MAX_=65536 第三种:使用tcmalloc来替代默认的ptmalloc2。google的tcmalloc提供更优的内存分配效率,性能更好,ThreadCache会阶段性的回收内存到CentralCache里。 解决了ptmalloc2中arena之间不能迁移导致内存浪费的问题。

    作者回复: 谢谢万历十五年同学的分享!

    2020-08-27
    10
  • 万历十五年
    计算机领域解决运算速度的两大法宝:1.加一层 2.化整为零。无论是cpu 寄存器, L1/2/3 cache,以及本节讲的glibc 内存池,都是通过“加一层”的方法,预先取得可能用到的资源,通过空间的代价来换得时间的效益。

    作者回复: 对的,“加一层”缓存:-)

    2020-08-27
    2
    8
  • 唐朝首都
    今天课程学习到了很多,感觉一整篇 的知识盲点,学完很受启发。另外有个问题:文中提到“预分配的 6GB 的 C 库内存池就与 JVM 中预分配的 8G 内存池叠加在一起,造成了 Java 进程的内存占用超出了预期。”中“预分配的6GB的C库内存池”为堆外内存么?如果代码中不适用堆外内存是不是就不会预分配6GB的C库内存池,还是说有那么多的线程就一定会使用到6GB的C库内存池??

    作者回复: 1、是的。 2、不使用堆外内存即可。

    2020-05-09
    2
    7
  • 中年男子
    用对象池 来节省 频繁创建、初始化对象造成的时间开销, 忆水寒提到的享元模式思想是对细粒度对象的共享和复用, 对象池是对享元模式的升级, 维护装载对象的池子, 提供获取、释放资源的方法。 感觉思考题的场景用对象池更为合适。

    作者回复: 是的

    2020-05-19
    2
  • kofssl
    确实写的很清晰,前面处理三方组件内存问题时,开始以为内存泄漏了,占用内存一点一点上去,就不释放,后来查代码也都没有明显的错误,最后是通过同事提到的内存碎片解决的,就用到了jemalloc的替换方式,同事是高手,你也是,哈哈

    作者回复: 呵呵,谢谢kofssl的夸奖。在你构建出分布式系统的完整执行路径后,相信面临其他疑难杂症时都会有明确的方向,可以google上自行找答案!

    2020-04-27
    2
  • 一凡
    每个子线程预分配的内存是 64MB 预分配什么意思,就是线程生成时候就分配的么?

    作者回复: 首次申请内存时分配的

    2020-07-20
    1
收起评论
显示
设置
留言
54
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部