经典的arena内存池实现-levelDB的内存池实现

news/2024/4/25 15:53:08/文章来源:https://blog.csdn.net/andrewgithub/article/details/128093780

Arena实现

arena可以说是解决内存碎片的利器,虽然有很多前辈说,要相信malloc的实现,你能想到的那些问题在设计Malloc的时候肯定都考虑到了。是的你可以相信malloc的实现,但是你不能对你自己有过分的自信,在功能比较复杂,特别是工作量比较大的时候,你不能保证你申请的每块内存都得到有效的释放,这个时候就不可避免的出现内存泄露。

arena解决的问题

  • 频繁分配内存,造成内存碎片化

  • 申请之后忘记释放,造成内存泄露

C语言接口与设计一书中,详细说明了Arena的设计,levelDB中可以说是简化了Arena的复杂度,只实现了内存的申请,没有实现内存的释放。

实现细节:

  1. 如果申请内存大于1024字节,直接申请一块指定大小的内存(默认4096),并将内存地址返回

  2. 如果申请的内存小于1024字节,则按照4096大小进行申请,返回指定大小内存,并记录剩余内存的大小,方便下次申请使用

适用场景:

Arena在事件处理、流水线处理、请求类型处理中有具有无可无可比拟的优势,事件开始,创建arena,中间过程无论那需要内存,只管申请,申请之后不用担心释放的事情,等到事件结束之后,只需要释放arena句柄就行了,即避免了内存碎片,又避免了内存泄露,同时也减轻了程序员的负担。

下面是levelDB中Arena实现的头文件

class Arena {
public:Arena();Arena(const Arena &) = delete;Arena &operator=(const Arena &) = delete;~Arena();// Return a pointer to a newly allocated memory block of "bytes" bytes.char *Allocate(size_t bytes);// Allocate memory with the normal alignment guarantees provided by malloc.char *AllocateAligned(size_t bytes);// Returns an estimate of the total memory usage of data allocated// by the arena.size_t MemoryUsage() const {return memory_usage_.load(std::memory_order_relaxed);}private:char *AllocateFallback(size_t bytes);char *AllocateNewBlock(size_t block_bytes);// Allocation statechar *alloc_ptr_;size_t alloc_bytes_remaining_;// Array of new[] allocated memory blocksstd::vector<char *> blocks_;// Total memory usage of the arena.//// TODO(costan): This member is accessed via atomics, but the others are//               accessed without any locking. Is this OK?std::atomic<size_t> memory_usage_;
};inline char *Arena::Allocate(size_t bytes) {// The semantics of what to return are a bit messy if we allow// 0-byte allocations, so we disallow them here (we don't need// them for our internal use).assert(bytes > 0);if (bytes <= alloc_bytes_remaining_) {char *result = alloc_ptr_;alloc_ptr_ += bytes;alloc_bytes_remaining_ -= bytes;return result;}return AllocateFallback(bytes);
}

可以看到,只是简单的实现了内存的申请,内存释放需要依赖析枸arena来一次性析枸,但这样已经足够内存数据库这种场景使用了。

下面来看下函数的具体实现,第一个就是头文件里面实现的Allocate函数

inline char *Arena::Allocate(size_t bytes) {// The semantics of what to return are a bit messy if we allow// 0-byte allocations, so we disallow them here (we don't need// them for our internal use).// 如果允许申请0字节的内存,会造成很多换乱的问题,因此内部接口中我们禁止这种使用方式,bytes要确保大于0assert(bytes > 0);// 如果上次申请的内存还够用if (bytes <= alloc_bytes_remaining_) {// 记录当前指针地址char *result = alloc_ptr_;// 将当前指针向后移动bytesalloc_ptr_ += bytes;// 剩余的内存大小在原有的基础上要减少 bytesalloc_bytes_remaining_ -= bytes;// 将地址放回return result;}// 如果上次申请剩余的内存不够,或者第一次进来申请内存,就新申请内存return AllocateFallback(bytes);
}

当申请的内存大于kBlockSize直接申请指定大小的内存,如果小于kBlockSize就一次申请kBlockSize大小的内存,将alloc_ptr_指向本次使用内存的结尾并使用alloc_bytes_remaining_记录剩余可用内存大小

static const int kBlockSize = 4096;

在创建Arena对象的时候,构造函数会将当前申请内存的指针赋值为空,剩余可用内存大小也设置为空,已使用的内存也设置为空,这些值的设置也符合我们常用的规范

Arena::Arena(): alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {}

在每次需要新申请内存的时候,我们都会把新申请的内存插入到blocks_中,这样在释放对象的析构函数中只需要将所有blocks_中的插入的对象进行释放即可将整个Arena存在期间申请的内存一次性释放干净。

Arena::~Arena() {for (auto & block : blocks_) {delete[] block;}
}

从上面的函数可以看出来,Allocate函数内部真正申请内存调用的是AllocateFallback函数,具体的实现如下:

char *Arena::AllocateFallback(size_t bytes) {if (bytes > kBlockSize / 4) {// Object is more than a quarter of our block size.  Allocate it separately// to avoid wasting too much space in leftover bytes.// 申请的原则// 如果申请的内存大于指定block size的四分之一,就按照指定内存大小进行申请char *result = AllocateNewBlock(bytes);return result;}// We waste the remaining space in the current block.// 如果需要的大小小于1024字节,那么按照个4096字节大小申请,alloc_ptr_ = AllocateNewBlock(kBlockSize);// 申请之后将remaining大小修改为申请的大小alloc_bytes_remaining_ = kBlockSize;char *result = alloc_ptr_;// 指针向前移动指定字节alloc_ptr_ += bytes;// 剩余可用内存减去已经使用的内存alloc_bytes_remaining_ -= bytes;return result;
}char *Arena::AllocateNewBlock(size_t block_bytes) {char *result = new char[block_bytes];// 将每次new出来的指针方能如到blocks中blocks_.push_back(result);// 已经使用的内存,是一个指针的大小和指定内存大小的和memory_usage_.fetch_add(block_bytes + sizeof(char *),std::memory_order_relaxed);return result;
}
char *Arena::AllocateAligned(size_t bytes) {// 如果当前系统指针大于8字节,就按照指针大小进行对齐,如果不是就按照8字节对齐const int align = (sizeof(void *) > 8) ? sizeof(void *) : 8;// 确保对齐字节大小是2的次方static_assert((align & (align - 1)) == 0,"Pointer size should be a power of 2");// 看当前的alloc_ptr_是否是8字节对齐size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - 1);// 如果 alloc_ptr_是8字节对齐,那么current_mod会等于0,slop也会是0,,如果slop是alloc_ptr_前进多少能够8字节对齐的位置size_t slop = (current_mod == 0 ? 0 : align - current_mod);// 实际需要自己的大小为slop和需要申请内存大小的和size_t needed = bytes + slop;char *result;if (needed <= alloc_bytes_remaining_) {result = alloc_ptr_ + slop;alloc_ptr_ += needed;alloc_bytes_remaining_ -= needed;} else {// AllocateFallback always returned aligned memoryresult = AllocateFallback(bytes);}// 确保申请内存的指针是8字节对齐assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);return result;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_38554.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

领悟《信号与系统》之 周期信号的傅里叶变换计算

周期信号的傅里叶变换计算一、周期信号的傅里叶变换存在的条件二、周期信号的傅里叶变换例题&#xff1a;一、周期信号的傅里叶变换存在的条件 典型非周期信号&#xff08;如指数信号&#xff0c;矩形信号等&#xff09;都是满足绝对可积&#xff08;或绝对可和&#xff09;条…

一种高选择性和灵敏的荧光生物标记物,可用于标记碱性磷酸酶 (ALP),5-FAM-Alkyne,510758-19-7,荧光生物标记物

【中文名称】5-羧基荧光素-炔烃【英文名称】 FAM alkyne,5-isomer&#xff0c;5-FAM alkyne【结 构 式】 【CAS号】510758-19-7 【分子式】C24H15NO6【分子量】413.39【基团】炔基基团【纯度】95%【规格标准】5mg&#xff0c;10mg&#xff0c;25mg&#xff0c;包装灵活&#x…

使用PyTorch实现简单的AlphaZero的算法(3):神经网络架构和自学习

神经网络架构和训练、自学习、棋盘对称性、Playout Cap Randomization&#xff0c;结果可视化 从我们之前的文章中&#xff0c;介绍了蒙特卡洛树搜索 (MCTS) 的工作原理以及如何使用它来获得给定棋盘状态的输出策略。我们也理解神经网络在 MCTS 中的两个主要作用&#xff1b;通…

xss-labs/level9

这一关界面感觉跟上一关很像 所以我们注入上一关的为编码的答案 javascript:alert(xss) 没能弹窗 查看源代码 他说我输入的链接不合法 我压根没有输入链接 我觉得后台应该是做了一个条件的判断 应该是要有链接才会在第二处输出点回显我们的输入 根据上面的猜测 我们构造如下…

Java.Integer.bitCount(int)源码解析

bitCount前言一、由易到难&#xff0c;头脑热身二、简单优化&#xff0c;一题多解三、分治优化四、bitCount(int)源码优化总结参考文献前言 如何求解一个二进制中1的个数&#xff1f;有常规的O(N)法&#xff0c;还有基于分治的O(logN)&#xff0c;即Java的bitCount(int)方法。…

我们为什么喜欢看疯狂科学家开飞艇?

很多人可能不是科幻迷&#xff0c;也在日常生活中接触过蒸汽朋克。为什么呢&#xff1f;很简单——蒸汽朋克几乎无处不在。相比其他科幻流派&#xff0c;蒸汽朋克可能算是最“出圈”的一种。简单地说&#xff0c;蒸汽朋克是一种科幻小说类型&#xff0c;由“蒸汽 ”(steam)和“…

《论文阅读》DeepSFM: Structure From Motion Via Deep Bundle Adjustment

留个笔记自用 DeepSFM: Structure From Motion Via Deep Bundle Adjustment 做什么 首先是最基础的&#xff0c;Structure-from-Motion&#xff08;SFM&#xff09;&#xff0c;SFM可以简单翻译成运动估计&#xff0c;是一种基于dui8序列图片进行三维重建的算法。简单来说就…

移动跨平台开发跨家选型参考建议

从 iPhone 诞生至今&#xff0c;智能手机风靡全球已将近20年&#xff0c;智能手机操作系统 iOS 和 Android 也成为当仁不让的顶流般的存在&#xff0c;而作为其背后的灵魂&#xff0c;移动应用也随着技术的发展已经越来越丰富。如果从技术层面来讲&#xff0c;移动 App 也从最开…

(1)点云库PCL学习——点云的格式、PCD文件的打开和显示

1、主要参考 (1)格式说明&#xff1a; 点云库PCL学习——点云的格式、PCD文件的打开和显示 ROS知识&#xff1a;点云文件.pcd格式_无水先生的博客-CSDN博客_pcd文件 &#xff08;2&#xff09;点云滤波&#xff0c;对nan的滤波 Python点云数据处理(三)滤波与RANSAC分割 - …

省 市 县 三级联动

大纲 一、导入省市县数据表(t_region) 二、引入jar包 三、导入所需util类&#xff08;整体框架&#xff09; 四、编写代码 1、配置数据库相关信息(数据库名、用户名、密码) config.propreties #oracle9i #driveroracle.jdbc.driver.OracleDriver #urljdbc:oracle:thin:loca…

jsp393学生宿舍管理系统mysql

两个权限 管理员和 学生 1. 学生信息管理 添加学生信息&#xff08;学生号&#xff0c;姓名 院系 班级入学日期 &#xff09;修改学生信息 学生退宿舍&#xff08;可以删除指定的学生也可以成批删除&#xff09; 2. 宿舍信息管理 宿舍的基本信息&#xff08;公寓数 宿舍…

第五届“强网”拟态防御国际精英挑战赛——特邀战队篇

第五届“强网”拟态防御国际精英挑战赛即将在南京隆重开赛&#xff01;本届大赛面向全球顶尖CTF战队&#xff0c;在创新应用场景与技术的基础上&#xff0c;拓展升级赛道&#xff0c;全面覆盖典型网络设备。大赛汇集国内外60支精英战队&#xff0c;参赛阵容、数量再创新高。 本…

科普下抖音的规则,为什么别人的内容很容易火,而我的很难?

今天给大家科普下抖音的规则&#xff0c;为什么别人的内容很容易火&#xff0c;而我的很难&#xff1f; 上一篇给大家讲了现在做抖音还来得及么&#xff1f;肯定的回答&#xff0c;一直都来得及。 既然来得及&#xff0c;那么我们怎么才能做好抖音呢&#xff1f; 在我看来&a…

5 - 2 单选题

1.下列线索二叉树中&#xff08;用虚线表示线索&#xff09;&#xff0c;符合后序线索树定义的是&#xff1a;B 后序线索二叉树的构建流程就是&#xff1a; 1.后序遍历二叉树&#xff1a;d b c a 2.第一个结点的前驱是NULL&#xff0c;即d的前驱&#xff0c;d的左孩子为NULL …

web表单(详解)

目录 1. 表单的概述 1.1 表单组成 2. 表单标记 2.1 input标记 2.2 select标记 2.3 textarea标记 3.HTML5新增标记 3.1 datalist标记 3.2 date 输入类型 3.3 color输入类型 3.4 button标记 3.5 details标记和summary标记 3.6 progress标记 3.7 meter标记 4 综合…

云原生微服务治理技术朝无代理架构的演进之路

摘要&#xff1a;本文基于对微服务治理技术从SOA, 微服务框架&#xff0c;到云原生架构的历史发展总结&#xff0c;提出了一种新的基于Javaagent技术的新一代无代理架构的服务治理技术&#xff0c;并介绍了其相关的代表性开源项目Sermant。本文分享自华为云社区《云原生微服务治…

Docker安装Redis集群失败经历汇总

在程序员的开发过程中&#xff0c;Redis可以说基本上是必不可少的缓存中间件。不管是二进制包还是docker安装Redis的文章在网上都是数不胜数。我之前自己玩Redis的时候基本不是二进制包安装就是docker安装&#xff0c;也没有尝试过集群方式。每次需要的时候&#xff0c;网上百度…

Cloud-computing 实验镜像 chinaskills_cloud_iaas.iso chinaskills_cloud_paas.iso

Cloud-computing 实验镜像 最近因新项目再次进行云计算环境的搭建&#xff0c; 找这两个镜像&#xff08; 找chinaskills_cloud_paas.iso chinaskills_cloud_iaas.iso&#xff09;颇为费劲&#xff0c;用尽九牛二虎之力总算找到了&#xff0c;该大侠还分享了诸多系统镜像和完…

Centos7搭建SVN代码控制服务器

Centos7搭建SVN代码控制服务器检查SVN是否安装创建SVN版本库配置代码库设置允许访问远程仓库的用户帐号密码设置权限控制设置SVN服务配置启动svn与停止启动SVN关闭SVN访问拉取远程仓库代码检查SVN是否安装 1、centos7系统自带SVN rpm -qa subversion2、如果没有则通过yum安装 …

Day15--加入购物车-初始化vuex

1.加入购物车&#xff1a; 我的操作&#xff1a; ************************************************************************************************************* 2.购物车里面的商品数据在多个页面都会用到。所以把购物车里面的商品数据存储在vuex里面&#xff0c; 我的…