项目介绍:
原型是goole的开源项目tcmalloc(全称:Thread-Caching Malloc),用于替代系统的内存分配相关的函数(malloc, free).知名度非常高。
项目要求知识储备和难度:
会用到C/C++、数据结构(链表、哈希桶)、操作系统内存管理、单例模式、多线程、互斥锁等等。
什么是内存池
池化技术
所谓“池华技术”,就是程序先向系统申请过量的资源,
内存池要解决的两个问题
一)效率问题:
举个例子,我们向家里要生活费,有两种方式;
每次要花钱时都去找爸爸妈妈。
一次把一个月的生活费拿完,自己每次要花钱的时候就去自己的小钱包就可以了。
内存池就是这样,一次申请过量的内存,要用的时候直接去自己申请的内存池中去拿就可以了。
二)内存碎片的问题:
内存碎片的分类:
a、外碎片
b、内碎片
malloc
C/C++中我们要动态申请都是通过malloc去申请内存的,但是我们要知道,实际我们不是直接去申请内存的,malloc实际就是一个内存池,如下图,malloc()相当于向操作系统“批发”了较大部分的内存,然后零售给程序用 ,不同平台实现的malloc有所不同,而我们这里介绍的tcmalloc是一种在多线程情况下更快的实现方式。
循序渐进---设计一个定长的内存池
我们知道申请内存使用的是malloc,malloc其实就是一个通用的大众货,什么场景下都可以使用,但是什么场景下都可以使用就意味着什么场景下都不会有很高的性能,下面我们来设计一个定长的内存池过过度,同时它是作为我们最终项目实现了一个基础组件。
解决问题:固定大小的内存申请释放需求
特点:
性能达到极致
不考虑内存碎片等问题
自由链表:管理那些换回来的内存块,将下一个内存块的地址存放在当前结点的头四个字节处。可以通过*(void**)来实现,原因是不同位的机器地址长度不同。后续的结点加入通过头插来实现。
高并发内存池整体设计框架
现代很多的开发环境都是多核多线程,在申请内存的场景下,必然存在激烈的锁竞争问题。malloc本身其实已经很优秀,那么我们项目的原型tcmalloc就是在多线程高并发的场景下更胜一筹,所以我们这次实现的内存池需要考虑以下几个方面的问题。
性能问题。
多线程环境下,锁竞争问题。
内存碎片问题。
concurrent memory pool主要由以下3个部分构成:
thread cache:线程缓存是每个线程独有的,用于小于256KB的内存的分配,线程从这里申请内 存不需要加锁,每个线程独享一个cache,这也就是这个并发线程池高效的地方。
2. central cache:中心缓存是所有线程所共享,thread cache是按需从central cache中获取的对 象。central cache合适的时机回收thread cache中的对象,避免一个线程占用了太多的内存,而 其他线程的内存吃紧,达到内存分配在多个线程中更均衡的按需调度的目的。central cache是存 在竞争的,所以从这里取内存对象是需要加锁,首先这里用的是桶锁,其次只有thread cache的 没有内存对象时才会找central cache,所以这里竞争不会很激烈。
3. page cache:页缓存是在central cache缓存上面的一层缓存,存储的内存是以页为单位存储及分 配的,central cache没有内存对象时,从page cache分配出一定数量的page,并切割成定长大小 的小块内存,分配给central cache。当一个span的几个跨度页的对象都回收以后,page cache 会回收central cache满足条件的span对象,并且合并相邻的页,组成更大的页,缓解内存碎片 的问题。
一)thread cache设计
二)central cache设计
三) page cache设计
四)基数树优化
总结:
tcmalloc和malloc相比优化地方在哪?
在多线程的条件下是无锁的,大大降低了锁的竞争性能消耗
解决了内存外碎片的问题
下面是我画的整个项目运行是的流程图,文字部分实在不想打第二遍,大家将就图看看吧
内存申请的大致过程:
内存释放的大致过程:
代码都放在码云了,大家需要的可以看看
项目代码:ConcurrentMemoryPool · 绅士.永/C++_Learn - 码云 - 开源中国 (gitee.com)