大家好,感谢邀请,今天来为大家分享一下深入解析内存池:理论与实践要点总结的问题,以及和的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
内存池如何设计;如何设计字节对齐;如何设计统计内存使用情况; (待完成)如何设计单元测试来验证内存池的正确性。 (待完成)一些技术问题:
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_没有对齐。
【深入解析内存池:理论与实践要点总结】相关文章:
用户评论
最近在研究内存管理,发现内存池真是个好东西!
有5位网友表示赞同!
想要了解一下内存池的使用方法,感觉可以提高程序效率。
有8位网友表示赞同!
之前不太了解内存池,标题很吸引人,一定要看看!
有9位网友表示赞同!
看文章总结的文章总 feels 好懂,就是想知道应用场景多多!
有15位网友表示赞同!
这篇文章的思路很有深度,让我对内存池有了更深的认识。
有16位网友表示赞同!
我一直用默认的内存管理方法,这个内存池应该是个很好的优化选择吧?
有18位网友表示赞同!
学习一下内存池的使用技巧,说不定能帮到我做算法。
有9位网友表示赞同!
分享一篇和内存池相关的文章,感觉很实用!
有9位网友表示赞同!
作者观察角度真是独特,分析得也挺到位。
有12位网友表示赞同!
这篇文章给我的启发很大,对内存管理有了更清晰的理解。
有10位网友表示赞同!
看这种总结类的文章确实方便,可以快速了解关键信息。
有12位网友表示赞同!
学习一下内存池和性能优化之间的关系,很有价值!
有17位网友表示赞同!
分享给正在学习C++的朋友,可能对他们很有帮助。
有16位网友表示赞同!
感觉内存池这个概念真挺有趣的,让我想要深入探索一下。
有18位网友表示赞同!
看标题就能明白文章内容,期待了解作者的观点和总结。
有10位网友表示赞同!
内存管理一直是重要的基础知识,学习这种优化方法确实好!
有14位网友表示赞同!
作者分析比较透彻,对不同类型的应用场景有针对性的探讨很实用。
有6位网友表示赞同!
感觉这篇文章适合作为学习内存池的入门资料。
有15位网友表示赞同!
很有意思的文章,让我对内存管理有了新的认识。
有16位网友表示赞同!