热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jemalloctcmalloc,tcmalloc内存池

最近学习了tcmalloc机制,它是go里面用到的内存分配机制。本文参考tcmalloc,加上一部分自己的理解。tcmallocVSptmalloc(glibc2.3mal


最近学习了tcmalloc的结构。 这是go中使用的内存分配机制。 本文以tcmalloc为参考,加入一些自己的理解。


tcmallocvsptmalloc (glibc 2.3 malloc )对于小内存,TCmalloc通过提供线程级内存分配来减少线程之间的争用。 ptmalloc2也提供线程级分配,但其内存分配给一个线程后,无法重新分配给另一个线程。 这会浪费很大的资源。 对于大容量内存,tcmalloc也采用了精细高效的分配策略。


在2.8 GHz P4环境下,tcmalloc执行小存储器malloc/free的时间约为50ns,小于ptmalloc2的300ns。


另外,关于空间利用,tcmalloc多余的空间很少,n个8字节对象占有的总空间约为8N*1.01,而ptmalloc2用4字节管理各个对象,因此空间利用率很小。


小内存分配和回收线程维护着用于小内存分配的cache。 如果需要请求内存,请先从线程cache分配,如果不够,则从Central Heap的central free lists分配。 另外,tcmalloc执行定期的垃圾回收,回收线程cache内的过剩内存并放入central free lists。


tcmalloc大约有88种大小的内存块(size-class ),小于1个块的大小会产生碎片性的浪费,但由于tcmalloc并不完全按2的指数倍分配,因此浪费的空间并不是那么大例如,如果内存块大小为8、16、32、48、64、80,而请求区域为70,则分配块大小为80即可,而无需分配128来浪费较大的碎片。 所有空闲块(class )都在单链表中维护,因此在使用时,只需直接从对应的链表中取出即可。


大致的分配策略如下


根据申请的大小,找到相应的块大小size-class。 如上图所示,如果在线程高速缓存链表中查找相应块大小的可用内存块的链表不为空,则返回。 线程内部没有冲突,所以不需要锁定。 如果链表为空


在central free lists中找到与size-class相对应的链表空闲块后,其结构与图线程缓存中的链表相匹配。 从central free lists自由链表中找到的空闲块放在线程高速缓存链表中,并从线程高速缓存链表返回到central free lists中相应的链表中


分配页面,将该页面划分为所需的多个size-class块,并将块放置在central free lists的空闲链表中。 将central free lists的空闲链表的一部分空闲块移动到线程缓存的链表中并从线程缓存的链表中返回,多级分配的优点是第一级是线程内部资源,锁定优先权是性能


线程的内部链表应该有多长? 如果小的话,就会从Central free list继续申请,如果引起竞争的大的话就会浪费。 分配策略采用慢启动增长的方式,防止了大量分配申请导致的系统抖动。 伪代码如下。


starteachfreelistmax _ length at1.allocationiffreelistempty { fetch min (max _ length,num _ objects _ to _ move ) from }超过}else{//num_objects_to_move时max_length =num_objects_to_move; } deallocationiflengthmax _ length {/don ' ttrytoreleasenum _ objects _ to _ moveifwedon ' thavethatmany.release mini num _ objects _ to _ move (objectststocestoced ) h长度//slove继续增长} else if max _ length num _ objects _ to _ move {/ifweconsistentlygoovermax _ length,shrink max _ th ifoverageskmaxoverages { max _ length-=num _ objects _ to _ move; overages=0; }

} } 中内存分配

  中内存指的是大小为256KB到1MB的大小,将会分配多个页,Central Heap以8k为一个页。Central Heap有一个包含128个链表的heap,链表大小从1个页到128个页。那么,如果申请的空间需要多少个page则从heap对应项中的链表中取出一个,如果该链表为空,则从下一个链表寻找(将会导致重新划分,比如需要4个页,但是4 pages链表为空,则从5pages中拿一个,但是5pages应该拆分为2部分,一个为4page,一个为1page)。如果都不满足需求,则视为“大内存”分配。
  注意,中内存不是从线程缓存中分配,而是直接在CentralHeap。

大内存分配

  大内存指的是大于1MB的块,这个将会从红黑树中进行分配。tcmalloc维护了一个红黑树用于标记不同的span,如果需要则从树种找到一个满足条件的最小的span进行分配。这可能导致span进行重新划分,一部分用于分配申请的大小,另外多出的部分可能重新放入到红黑树,也可能放入全局Centeral Heap的center free-lists(上图所示)。如果红黑树中找不到满足的span,则从系统中进行申请(tcmalloc采用sbrk和mmap)

Span

  span对象负责管理连续的多个页,比如下图,a,b,c,d是4个span,管理不同大小的连续页。
  所有span组成一个span lists,span内部划分给多个对象使用,参加下图,图来自图解 TCMalloc
  一个span可能被分配或者释放。如果释放了,挂入到page heap的链表中。如果申请了,可能作为大内存被分配,也可能被划分后进行分配,比如划分为多个小内存。如果被划分成小内存,则内部会记录各个size-class的分配情况。
  同样还是上面第一个图,可以用一个数组记录各个页分别属于哪个span。但是这样对空间消耗比较大,每一块内存都需要一个数组元素表示,tcmalloc用基数radix tree(前缀树/字典树trie tree的一种压缩优化)记录映射情况,这样可以对前缀相同部分进行压缩。如果是32位系统,则树高2层,根节点包含了32项,叶子节点包含2^14项,则对32位系统来说,可以记录2^19个页(2^19 * 8k=2^32)。如下图所示,采用基数而不是前缀树的原因是为了空间压缩,如果是前缀树,则需要2^32 * 2 - 1=2^33-1个结点,就算结点内只有一个值和一个指针,存储这么多指针也需要浪费很多的空间。下图是手绘的radix tree示意图。
  对于64位系统,则树高3层。

回收

  当一个对象被释放后,通过计算页号,再通过radix tree得知所在的span,span可以知道是否为小内存,如果是的话还知道size-class(span内部维护该信息)。当然,在开始的时候说过,如果是小内存,释放会挂入线程内部的缓存链表,知道链表大小超过了阈值则会回收到central free lists。当然central free lists也可能发生回收,属于一个span的内存全部空闲后,将会触发回收。
  如果是大内存,则通过span获取页跨度,假设为[p,q],那么寻找p-1和q+1所在的span是否为空,如果都为空,则进行合并后放入page heap(上面提到的红黑树)。

central free lists

  上面我们提过,我们在central free lists中保存了不同size-class小内存用于二次分配。每个central free lists由二级结构组成,第一级是span,第二级是span内空闲空间组成的链表,保存小内存地址。如果线程内部小内存不够用,就从这里进行分配。如果线程内部小内存过多就行回收,也挂入这里的链表。如果一个span内的内存全都回收后,该span也将进行回收,放入到全局的page heap中。

线程缓存的垃圾回收

  在开始讲小内存的伪代码中已经介绍过垃圾回收的机制。当线程缓存超过max_size以后,将会触发回收。垃圾回收只有在对象释放的时候才会发生。用户可以通过控制tcmalloc_max_total_thread_cache_bytes来控制num_objects_to_move的大小。每次垃圾回收,线程将尝试将自己的max_size变大。如果回收时max_size小于tcmalloc_max_total_thread_cache_bytes,那么max_size将会直接增长;反之,则会促使从别的线程中偷取max_size。
此处存在一个疑问:这种机制将会导致max_size超过tcmalloc_max_total_thread_cache_bytes,和上面伪代码中的收缩机制有什么区别?什么时候用收缩,什么时候从别的线程进行偷取?

说明

转载请注明链接:http://vinllen.com/tcmallocqian-xi/

参考:

https://gperftools.github.io/gperftools/tcmalloc.html
https://github.com/gperftools/gperftools/blob/master/src/tcmalloc.cc
https://zhuanlan.zhihu.com/p/29216091


推荐阅读
  • golang反射,golang反射性能
    本文目录一览:1、关于反射2、尝试用golan ... [详细]
  • Opencv提供了几种分类器,例程里通过字符识别来进行说明的1、支持向量机(SVM):给定训练样本,支持向量机建立一个超平面作为决策平面,使得正例和反例之间的隔离边缘被最大化。函数原型:训练原型cv ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 导出功能protectedvoidbtnExport(objectsender,EventArgse){用来打开下载窗口stringfileName中 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 本文详细介绍了使用C#实现Word模版打印的方案。包括添加COM引用、新建Word操作类、开启Word进程、加载模版文件等步骤。通过该方案可以实现C#对Word文档的打印功能。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文介绍了在实现了System.Collections.Generic.IDictionary接口的泛型字典类中如何使用foreach循环来枚举字典中的键值对。同时还讨论了非泛型字典类和泛型字典类在foreach循环中使用的不同类型,以及使用KeyValuePair类型在foreach循环中枚举泛型字典类的优势。阅读本文可以帮助您更好地理解泛型字典类的使用和性能优化。 ... [详细]
  • 微服务之总体架构篇
    一、单体架构存在的问题缺点:1、难以维护:当单体应用业务不断迭代后代码量非常臃肿,模整个项目非常复杂,每次更改代码都可能带来新的bug;2、部署项目麻烦:庞大之后项目部署效率 ... [详细]
author-avatar
loveyao123456
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有