作用
PageHeap在TCMalloc中主要作用:
- 作为Central Cache和操作系统之间的内存缓存,PageHeap对内存的管理是通过Span来管理的,而Central Cache向pageheap申请内存的基本单位就是span。关于Span的描述见上篇CentralCache。
- 负责大块内存的申请和释放。
空闲span的管理
PageHeap有两种span的管理方式:
- 一个是用于管理内存小于等于1M的连续页内存,即SpanList free_[kMaxPages]。SpanList free_[kMaxPages]数组是按页大小递增的,即free_[1]是存放管理1页的Span,free_[2]是存放管理2页的Span,依此类推。
- 另一个是那些内存大于1M的连续页内存,即SpanList large_。
SpanList结构下面有两个Span链表,这两个链表作用是不同的,Span normal存放的那些还没有释放给系统的Span,而Span returned则存放的是那些已经释放给系统的Span。
span的三种状态
struct SpanList {
Span normal;// 该链表中的span对应的物理内存是空闲的,和地址空间也可以正常使用
Span returned;//该链表中的span对应的物理内存已经交还给内核,但是地址空间还被占用并未释放,(通过API madvise函数实现)
};
span的状态只有三种:
- 一种是IN_USE表示正在被使用,也就是分给central cache用的;
- 一种表示ON_NORMAL_FREELIST表示放在了normal freelist上面。
另外一种是ON_RETURNED_FREELIST表示放在returned freelist上面。
(TCMalloc调用内存释放的接口TCMalloc_SystemRelease,而对应的系统调用的接口是madvise(),建议系统的行为是MADV_FREE,而MADV_FREE则将这些页标识为延迟回收。当内核内存紧张时,这些页将会被优先回收,如果应用程序在页回收后又再次访问,内核将会返回一个新的并设置为0的页。而如果内核内存充裕时,标识为MADV_FREE的页会仍然存在,后续的访问会清掉延迟释放的标志位并正常读取原来的数据,因此应用程序不检查页的数据,就无法知道页的数据是否已经被丢弃。
因为 Linux 不支持 MADV_FREE,所以使用了 MADV_DONTNEED。使用 MADV_DONTNEED调用 madvise,告诉内核这段内存今后”很可能”用不到了,其映射的物理内存尽管拿去好了,因此,TCMalloc_SystemRelease 只是告诉内核,物理内存可以回收以做它用,但虚拟空间还留着,下次访问是时会产生缺页中断,而重新申请物理内存。)
被TCMalloc_SystemRelease的span都挂在了returned freelist上面
PageMap pagemap_
pagemap_是作为PageID(即页ID)和它归属的Span之间的映射表,TCMalloc为32位系统和64为系统设计不同数据结构,32位系统使用二级的Radix Tree(TCMalloc_PageMap2),而64位系统使用三级Radix Tree(TCMalloc_PageMap3)。对不同的系统采用不同的数据结构主要是考虑这个映射表占用内存的大小。
在当前的x86_64处理器中,只用了地址的低48bits用于虚拟和物理地址的转换,高16bits是不用的。所以在8K页