PageCache
为什么最大的span大小是128页?答:这个最大页可以根据场景需求设置;对于CentralCache单次最多申请256KB来说,128页已经足够了。
申请内存:
- 当centralcache向pagecache申请内存时,pagecache先检查对应位置有没有span,如果没有则向更大页寻找一个span,如果找到则分裂成两个。比如:申请的是4页page,4页page后面没有挂span,则向后面寻找更大的span,假设在10页page位置找到一个span,则将10页page span分裂为一个4页page span和一个6页page span。
- 如果找到_spanList[128]都没有合适的span,则向系统使用mmap、brk或者是VirtualAlloc等方式申请128页page span挂在自由链表中,再重复1中的过程。
- 需要注意的是central cache和pagecache的核心结构都是spanlist的哈希桶,但是他们是有本质区别的,central cache中哈希桶,是按跟thread cache一样的字节大小对齐关系映射的,他的spanlist中,挂的是span中的内存都被按映射关系切好,链接成小块内存的自由链表。而page cache 中的spanlist则是按一个span的页数 作下标桶号映射的,也就是说第 i 号桶中挂的span都是 i 页大小的span块。
释放内存:
- 如果centralcache释放回一个span,则依次寻找这个span前后的pageid没有在使用的空闲span,看是否可以合并,如果可以,则合并+继续向前寻找。这样就可以将切小的内存合并收缩成大的span,放到后面大的page桶中,减少内存碎片。
在一开始PageCache中没有任何span,用户向ThreadCache+CentralCache申请不到,再向PageCache申请时,PageCache对象会先向堆申请一个page_hash中最大页的span内存块,然后CentralCache申请的是多大的内存块,就把最大页的span内存块分割成多大的,供它使用。
不用桶锁原因:
ThreadCache向CentralCache申请不到就去PageCache申请,多个线程在PageCache中进行分割/插入/获取等操作时,可能会对同一个桶操作,这时要加锁。
桶锁 VS 整个hash一把大锁 ?
在CentralCache中申请一次空间,只用访问对应的一个桶,用桶锁挺好,只用加/解一次锁,同时其它线程也可以访问其它桶;
但PageCache中向后找较大的span + 分割成两个小span插入两个较小page的桶(或 把回收的小span合并成大span),如果用桶锁,每次申请需要频繁加/解多次锁,影响效率,一把大锁更方便。
用桶锁,管理复杂,频繁加解锁有性能消耗。
- 以页为单位的大内存管理span的定义及spanlist定义。
- PageCache中查找获取一个k页的span;1.直接从后面找到大块;2.没找到,向堆申请;
- ThreadCache向访问CentralCache对应位置上的桶(加桶锁),申请内存块,没空余内存块时,(先解桶锁,方便其它线程访问这个桶归还内存块)再访问PageCache(加大锁),申请内存块。