大家好,感谢邀请,今天来为大家分享一下深入解析内存池:理论与实践要点总结的问题,以及和的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
内存池如何设计;如何设计字节对齐;如何设计统计内存使用情况; (待完成)如何设计单元测试来验证内存池的正确性。 (待完成)一些技术问题:
Malloc已经实现了内存对齐。如何实现以及如何证明? (未解决)free和delete是如何实现的,如何知道要释放多少内存。 (未解决)
一 如何设计内存池
内存池一般来说,大块内存是直接分配的,而小块内存则先申请一大块内存,然后继续使用这大块内存,直到用完为止。基本算法如下:
内联字符* Arena:分配(size_t字节){
//如果我们允许的话,返回内容的语义有点混乱
//0 字节分配,所以我们在这里不允许它们(我们不需要
//它们供我们内部使用)。
断言(字节0);
if (bytes=alloc_bytes_remaining_) { //是否可以从可用内存中获取
char* 结果=alloc_ptr_;
alloc_ptr_ +=字节;
alloc_bytes_remaining_ -=字节;
返回结果;
}
返回AllocateFallback(字节); //否,申请内存
不能从可用内存中获取大内存块。对于小块内存,需要查看剩余内存大小。如果剩余金额少于需求量,也需要申请。否则,无需申请。
因此,小内存共享大内存的使用情况需要通过以下两个变量来标识:
//分配状态
字符* alloc_ptr_; //大块内存的可用位置;
size_t alloc_bytes_remaining_; //当前大内存块还剩下多少?这简化了小内存块的分配。大块内存的分配逻辑如下:
char* Arena:AllocateFallback(size_t 字节) {
if (字节kBlockSize/4) {
//对象超过块大小的四分之一。单独分配
//避免在剩余字节中浪费太多空间。
char* 结果=AllocateNewBlock(字节);
返回结果;
}
//我们浪费了当前块中的剩余空间。
alloc_ptr_=分配新块(kBlockSize);
alloc_bytes_remaining_=kBlockSize;
char* 结果=alloc_ptr_;
alloc_ptr_ +=字节;
alloc_bytes_remaining_ -=字节;
返回结果;
}
char* Arena:AllocateNewBlock(size_t block_bytes) { //所有申请内存的操作都在这里;
char* 结果=new char[block_bytes];
block_.push_back(结果);
memory_usage_.NoBarrier_Store(
reinterpret_cast(MemoryUsage() + block_bytes + sizeof(char*)));
返回结果;
}申请大块内存有两种情况:
(1)用户自行申请大内存;
(2)用户申请了小内存,但是小内存使用的大内存块已经用完。
对于第一种情况,直接调用AllocateNewBlock方法,申请那块大内存,交给用户即可。
对于第二种情况,调用AllocateNewBlock方法申请固定大小内存(kBlockSize)后,还需要设置alloc_ptr_和alloc_bytes_remaining_变量;最后在这个新申请的大内存中给用户分配内存。
内存的释放是一起执行的,位于分配器的析构函数中:
竞技场:~竞技场() {
for (size_t i=0; i 块_.size(); i++) {
删除[]块_[i];
}
}blocks_ 定义如下:
//new[] 分配的内存块数组
std:vectorblocks_;blocks_包含AllocateNewBlock方法申请的所有内存,最终被统一释放。
一些问题:
小内存共享的大内存块有多大,即一次申请多大的大小合适? (相关的论文查询)
这是leveldb 中的大小: static const int kBlockSize=4096;应该申请多少内存并直接返回给用户,而不是共享一个大内存块?
这又分为两种情况:
(1)当有大块内存可用时,只要大块内存足够,就会直接返回给用户;
(2)当没有可用的大块内存,并且超过kBlockSize的1/4时,直接申请相应大小的内存并返回给用户,而不是申请kBlockSize大小的内存作为小内存。
上述策略涉及到内存的浪费,具体如下:
当用户新申请的内存不在可用内存大小范围内且小于kBlockSize时,则该段可用内存被浪费,并申请新的内存,alloc_ptr_从新的内存开始。
内存是最后统一释放的,那么有没有什么策略需要提前释放以节省内存呢?具体发布时间是什么时候?
二 如何设计字节对齐
malloc分配的内存必须是对齐的,也就是说返回的地址必须是2的幂(内存对齐的大小一般是指针的大小,指针的大小必须是2的幂)。
那么如何找到对齐的尺寸align:
const int 对齐=(sizeof(void*) 8) ? sizeof(void*) : 8;对齐后的地址必须满足(align - 1的值中1的个数代表返回值指针中最后一个0的个数):
断言((reinterpret_cast(结果)(align-1))==0);具体算法如下:
char* Arena:AllocateAligned(size_t 字节) {
const int 对齐=(sizeof(void*) 8) ? sizeof(void*) : 8;
断言((对齐(align-1))==0); //指针大小应该是2的幂
size_t current_mod=reinterpret_cast(alloc_ptr_) (align-1); //alloc_ptr_的最后n位,n为对齐的位数
size_t slop=(current_mod==0 ? 0 : 对齐- current_mod); //对齐所需的空闲字节数
所需的size_t=字节+ slop;
字符* 结果;
if (needed=alloc_bytes_remaining_) { //与上一节中的Allocate类似
结果=alloc_ptr_ + slop;
alloc_ptr_ +=需要;
alloc_bytes_remaining_ -=需要;
} 别的{
//AllocateFallback 总是返回对齐的内存
结果=AllocateFallback(字节); //对malloc的调用必须是内存对齐的;
}
断言((reinterpret_cast(结果)(align-1))==0);
返回结果;
}函数也分为两部分进行处理:
(1)申请内存大小+对齐字节数。可用内存大小。这时候直接调用malloc申请(这样可能会造成内存的浪费)。这时候就必须进行内存对齐;
(2)申请内存大小+对齐字节数、可用内存大小、手动对齐:调整alloc_ptr_到对齐位置,然后调整alloc_ptr_到下一个可用位置作为返回结果。对齐的结果与返回值对齐,最终的alloc_ptr_没有对齐。
【深入解析内存池:理论与实践要点总结】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
最近在研究内存管理,发现内存池真是个好东西!
有5位网友表示赞同!
想要了解一下内存池的使用方法,感觉可以提高程序效率。
有8位网友表示赞同!
之前不太了解内存池,标题很吸引人,一定要看看!
有9位网友表示赞同!
看文章总结的文章总 feels 好懂,就是想知道应用场景多多!
有15位网友表示赞同!
这篇文章的思路很有深度,让我对内存池有了更深的认识。
有16位网友表示赞同!
我一直用默认的内存管理方法,这个内存池应该是个很好的优化选择吧?
有18位网友表示赞同!
学习一下内存池的使用技巧,说不定能帮到我做算法。
有9位网友表示赞同!
分享一篇和内存池相关的文章,感觉很实用!
有9位网友表示赞同!
作者观察角度真是独特,分析得也挺到位。
有12位网友表示赞同!
这篇文章给我的启发很大,对内存管理有了更清晰的理解。
有10位网友表示赞同!
看这种总结类的文章确实方便,可以快速了解关键信息。
有12位网友表示赞同!
学习一下内存池和性能优化之间的关系,很有价值!
有17位网友表示赞同!
分享给正在学习C++的朋友,可能对他们很有帮助。
有16位网友表示赞同!
感觉内存池这个概念真挺有趣的,让我想要深入探索一下。
有18位网友表示赞同!
看标题就能明白文章内容,期待了解作者的观点和总结。
有10位网友表示赞同!
内存管理一直是重要的基础知识,学习这种优化方法确实好!
有14位网友表示赞同!
作者分析比较透彻,对不同类型的应用场景有针对性的探讨很实用。
有6位网友表示赞同!
感觉这篇文章适合作为学习内存池的入门资料。
有15位网友表示赞同!
很有意思的文章,让我对内存管理有了新的认识。
有16位网友表示赞同!