TrafficServer内存分配器优化
淘仇恕(张云开)
为方便不能翻墙的同学,在这里放一份,原文及未来的更新见Google Drive链接
TS分配器简介
TrafficServer(简称TS)的内存分配器,在代码里的入口函数为ink_freelist_new/ink_freelist_free,与传统的malloc/free对应。另外还有一个初始化函数:ink_freelist_init,用于设定内存池的元素大小、个数。
TS的内存分配器,其实是一个“类级别”的内存池。用户在使用时,需要为不同的类创建各自的分配器实例。通过要求不同的类,使用各自的分配器实例,从而简化了分配器的设计——每个分配器实例的内存池中,元素大小(tyep_size,其实就是用户sizeof(class)的大小)相等,这样就不必考虑不同大小的元素的合并问题。
在每个分配器实例的内部,有一个lockfree的链表L,多线程可以同时访问链表L而无需加锁,这正是TS分配器的高效之处。简而言之,TS分配器的工作过程是这样的:
申请内存
- 偿试从链表L的头部Pop一个空闲块。若链表L的头部被其他线程占用,循环第1步。
- 若链接表L的头部为空:通过malloc申请一个大块(Chunk),把这个Chunk切分为chunk_size份,每份大小为type_size的小块。把这些小块Push到L中,回到第1步。
- 若链接L头部不为空,把头部的块地址,返回给用户,结束。
释放内存
- 偿试把用户返回的空闲块Push到链接L的头部。若链表L的头部被其他线程占用,循环第1步。
- 否则,Push成功,结束。
TS分配器的优缺点
优点
由于采用lockfree的链表,线程之间冲突降到了最小,而且申请与释放的逻辑非常简单,速度非常快。
缺点
从TS分配器的申请、释放过程,可以发现,该分配器不存在回收策略。当多个线程同时访问到一个空的链表,这些线程就会同时新建一个Chunk,这也许是导致内存过度膨涨的原因之一,虽然概率很小。无论如何,一个不具备回收策略的分配器,随着时间地推移,它的内存会越来越大,完全不受控制,最终将导致程序OOM。
TS的内存使用特征
一句话概括,就是“过山车”式的。经过统计发现,当一个外部的请求过来,TS会不断的申请内存,随着请求的完成,又会不断的释放内存。整个内存的使用曲线,总是由零点到波峰再到零点。下图是从测试环境(通过jtest工具)中,取出的TS内存分配曲线(某个线程下某个InkFreeList内存池的使用情况):
图1,TS的内存使用特征