全局内存分配器:tcmalloc_sys_alloc
来源:互联网 发布:淘宝网店上货教程 编辑:程序博客网 时间:2024/04/30 07:01
[TCMalloc] 全局内存分配器
TCMalloc 的全局分配器,处于 TCMalloc 的最底层,负责向操作系统申请和释放内存,接口有两个,定义在 src/system-alloc.h|.cc:
extern void* TCMalloc_SystemAlloc(size_t bytes, size_t *actual_bytes, size_t alignment = 0);extern bool TCMalloc_SystemRelease(void* start, size_t length);
TCMalloc_SystemAlloc 的参数,除了要申请的大小 bytes,还有 actual_bytes 和 alignment,因为对齐的需求,该接口可能分配大于 bytes 的内存,实际大小保存在 actual_bytes。TCMalloc_SystemRelease 负责『释放』内存,至于释放为什么加引号,下面会提到。接下来,介绍下这两个接口的实现。
malloc-extension.h 里面,定义了接口类 SysAllocator,该类只有一个接口:
class SysAllocator { public: SysAllocator() {} virtual ~SysAllocator(); virtual void* Alloc(size_t size, size_t *actual_size, size_t alignment) = 0;};
system-alloc.cc 里面,对 SysAllocator 有三个实现:SbrkSysAllocator, MmapSysAllocator, DefaultSysAllocator。SbrkSysAllocator 和 MmapSysAllocator 分别使用 sbrk 和 mmap 向系统分配内存,这两个类没有任何数据成员。DefaultSysAllocator 是 前两种 Allocator 的委托:
class SbrkSysAllocator : public SysAllocator {public: SbrkSysAllocator() : SysAllocator() { } void* Alloc(size_t size, size_t *actual_size, size_t alignment);}; class MmapSysAllocator : public SysAllocator {public: MmapSysAllocator() : SysAllocator() { } void* Alloc(size_t size, size_t *actual_size, size_t alignment);}; class DefaultSysAllocator : public SysAllocator { public: DefaultSysAllocator() : SysAllocator() { for (int i = 0; i < kMaxAllocators; i++) { failed_[i] = true; allocs_[i] = NULL; names_[i] = NULL; } } void SetChildAllocator(SysAllocator* alloc, unsigned int index, const char* name) { if (index < kMaxAllocators && alloc != NULL) { allocs_[index] = alloc; failed_[index] = false; names_[index] = name; } } void* Alloc(size_t size, size_t *actual_size, size_t alignment); private: static const int kMaxAllocators = 2; bool failed_[kMaxAllocators]; SysAllocator* allocs_[kMaxAllocators]; const char* names_[kMaxAllocators];};static char sbrk_space[sizeof(SbrkSysAllocator)];static char mmap_space[sizeof(MmapSysAllocator)];static char default_space[sizeof(DefaultSysAllocator)];
在 libtcmalloc 初始化的时候,使用 new 操作符的 placeholder 形式,分别在 sbrk_space, mmap_space, default_space 上面初始化这三个 Allocator。然后调用 DefaultSysAllocator 的 SetChildAllocator 接口,将 SbrkSysAllocator 和 MmapSysAllocator 设置成待用分配器:
void InitSystemAllocators(void) { MmapSysAllocator *mmap = new (mmap_space) MmapSysAllocator(); SbrkSysAllocator *sbrk = new (sbrk_space) SbrkSysAllocator(); DefaultSysAllocator *sdef = new (default_space) DefaultSysAllocator(); if (kDebugMode && sizeof(void*) > 4) { sdef->SetChildAllocator(mmap, 0, mmap_name); sdef->SetChildAllocator(sbrk, 1, sbrk_name); } else { sdef->SetChildAllocator(sbrk, 0, sbrk_name); sdef->SetChildAllocator(mmap, 1, mmap_name); } sys_alloc = tc_get_sysalloc_override(sdef);}
DefaultSysAllocator 的 Alloc 接口是这样实现的:
void* DefaultSysAllocator::Alloc(size_t size, size_t *actual_size, size_t alignment) { for (int i = 0; i < kMaxAllocators; i++) { if (!failed_[i] && allocs_[i] != NULL) { void* result = allocs_[i]->Alloc(size, actual_size, alignment); if (result != NULL) { return result; } failed_[i] = true; } } // After both failed, reset "failed_" to false so that a single failed // allocation won't make the allocator never work again. for (int i = 0; i < kMaxAllocators; i++) { failed_[i] = false; } return NULL;}
先后尝试使用 SbrkSysAllocator 和 MmapSysAllocator 分配内存,如果某个分配器分配失败,则标记相应的 failed_ 域,下次便不从这个分配器分配内存。如果全部失败,则返回 NULL,同时清除 failed_ 域,以免无法恢复。TCMalloc_SystemAlloc 函数分配内存时候,调用全局的 sys_alloc->Alloc() 即可。
关于全局内存的分配要介绍的就这么多,至于 SbrkSysAllocator 和 MmapSysAllocator 的 Alloc 实现细节,但凡对 sbrk 和 mmap 了解的人一猜就知道了。
下面介绍下内存的释放。可以注意到,前面的 Allocator 只有 Alloc 接口,并没有一个 Release 或者 Free 接口,那么内存是怎么释放的呢?事实上,TCMalloc 从来不释放内存!
那么,TCMalloc_SystemRelease 做些什么呢?我们知道,无论是 brk,还是 mmap,申请的都是虚拟内存,物理内存是由内核在缺页中断时按需分配的。TCMalloc_SystemRelease 并没有调用 sbrk 和 munmap 将虚拟内存『归还』给操作系统,而是调用 madvise 系统调用。
#if !defined(MADV_FREE) && defined(MADV_DONTNEED)# define MADV_FREE MADV_DONTNEED#endifbool TCMalloc_SystemRelease(void* start, size_t length) {#ifdef MADV_FREE const size_t pagemask = pagesize - 1; size_t new_start = reinterpret_cast<size_t>(start); size_t end = new_start + length; size_t new_end = end; // Round up the starting address and round down the ending address // to be page aligned: new_start = (new_start + pagesize - 1) & ~pagemask; new_end = new_end & ~pagemask; if (new_end > new_start) { int result; do { result = madvise(reinterpret_cast<char*>(new_start), new_end - new_start, MADV_FREE); } while (result == -1 && errno == EAGAIN); return result != -1; }#endif return false;}
因为 Linux 不支持 MADV_FREE,所以使用了 MADV_DONTNEED。使用 MADV_DONTNEED 调用 madvise,告诉内核这段内存今后『很可能』用不到了,其映射的物理内存尽管拿去好了!
因此,TCMalloc_SystemRelease 只是告诉内核,物理内存可以回收以做它用,但虚拟空间还留着。
那么,是不是 TCMalloc 永远不会归还虚拟空间呢?一般来说,是的。但当虚拟空间耗尽,或者虚拟空间有碎片,无法满足内存需要时,TCMalloc 会试图合并,然后把所有空闲的虚拟空间归还,然后从新分配。
- 全局内存分配器:tcmalloc_sys_alloc
- 内存分配器
- 内存分配器
- 内存分配器
- 如何替换vc静态库里的全局内存分配器(libcmt.lib)
- 简单内存分配器
- ACE内存分配器一
- ACE内存分配器二
- ACE内存分配器三
- STL的内存分配器
- STL中的内存分配器
- 快速的内存分配器
- GFP-Tree 内存分配器
- STL的内存分配器
- linux启动内存分配器
- [译文]SLUB 内存分配器
- TrafficServer内存分配器优化
- STL的内存分配器
- 预告 | 美团云人工智能峰会将于10月31日举行 宣布AI共享平台新进展
- 对话 | 科大讯飞胡郁:加紧布局“平台+赛道”抢占AI红利
- 缤果盒子关闭上海首批无人店 谷歌Intel研发手机AI芯片
- 从乔布斯到林志颖 每个科技公司都住着一个“时尚大魔头”
- MYSQL双机热备
- 全局内存分配器:tcmalloc_sys_alloc
- 用PS制作简单婚纱海报
- 在线判题(字符串)
- spring来进行正式、测试环境无缝式切换
- 如何搭建一个独立博客——简明Github Pages与Hexo教程
- cut命令
- Codeforces 879C Short Program【思维】
- 647. Palindromic Substrings (DP)
- @Transactional(rollbackFor=Exception.class)的使用