LevelDB Arena源码分析
欢迎访问我的个人博客
https://vincillau.github.io/
文章目录
- 欢迎访问我的个人博客
- https://vincillau.github.io/
- LevelDB Arena源码分析
- 什么是Arena
- Arena的实现思路
- Arena的实现
- Allocate的实现
- AllocateAligned的实现
- 总结
- 参考
LevelDB Arena源码分析
什么是Arena
Arena
是LevelDB
中实现的一个简易的内存池
。因为LevelDB是一个key-value数据库
,所以当为较小的key或value分配内存时可能会引起内存碎片
以及性能问题
(频繁调用new和delete)。Arena就是为了解决这些问题的。Arena的实现非常简洁,不过100多行C++代码,十分适合学习。下面我们就一起了解一下Google大佬们管理内存的方法吧。
Arena的实现思路
既然分配较小的内存会导致产生内存碎片,那么我们可以先分配一块较大的内存块,然后在将这个内存块分割成若干个较小的内存块分配给使用者来存储较小的key或value,这就是Arena的基本思路。
首先我们来看一下Arena的几个成员变量,它们指示了Arena当前的状态:
char* alloc_ptr_; // 内存块未分配内存的起始地址
size_t alloc_bytes_remaining_; // 内存块中未分配的字节数
std::vector<char*> blocks_; // Arena已申请的内存块
std::atomic<size_t> memory_usage_; // Arena的内存使用量
来看下面一张图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWKfCVX3-1634956791565)(/myresource/img/arena.png)]
Arena的blocks_
成员存储着Arena已经申请的内存块。alloc_ptr_
指向当前内存块中尚未分配空间的起始地址。当需要Arena分配n个字节的内存时,如果n大于1024(也就是1KB),则Arena会申请一个大小为n的内存块并返回内存块的地址,alloc_ptr_
和alloc_bytes_remaining_
的值都不会改变(之所以这样做是为了避免浪费大量内存)。如果n小于1024并且当前块中的内存块中剩余的字节数大于或等于n,那么Arena会以alloc_ptr_
为起始地址分配n个字节的内存返回给申请者,并将alloc_ptr_
向后移动n个字节指向新的未分配空间的起始地址。如果内存块剩余的空间小于n,则Arena会申请新的一块大小为4KB的内存块给调用者分配内存,之前的内存块中尚未分配的内存就浪费了。所以Arena最多会浪费1/4的内存。
Arena的实现
Arena为使用者提供了三个成员函数接口:
char* Allocate(size_t bytes);char* AllocateAligned(size_t bytes);size_t MemoryUsage() const {return memory_usage_.load(std::memory_order_relaxed);
}
Allocate
用于为调用者分配bytes个字节的内存,AllocateAligned
则为调用者分配bytes个字节的对齐的内存,MemoryUsage
用于查看该Arena对象使用的内存总量。我们重点看一下Allocate
和AllocateAligned
的实现:
Allocate的实现
inline char* Arena::Allocate(size_t bytes) {assert(bytes > 0);if (bytes <= alloc_bytes_remaining_) {char* result = alloc_ptr_;alloc_ptr_ += bytes;alloc_bytes_remaining_ -= bytes;return result;}return AllocateFallback(bytes);
}
Allocate
首先会判断一下当前块中剩余的空间是否足够分配bytes个字节的内存。如果空间足够,Allocate会将alloc_ptr_
向后移动bytes个字节让其指向新的未分配的内存的起始地址,并将alloc_bytes_remaining_
减小bytes,然后返回分配的内存。如果当前块的剩余空间不足以分配bytes字节的内存,则Allocate会调用AllocateFallback
申请新的内存块分配内存。下面是AllocateFallback
的源码:
char* Arena::AllocateFallback(size_t bytes) {if (bytes > kBlockSize / 4) {char* result = AllocateNewBlock(bytes);return result;}alloc_ptr_ = AllocateNewBlock(kBlockSize);alloc_bytes_remaining_ = kBlockSize;char* result = alloc_ptr_;alloc_ptr_ += bytes;alloc_bytes_remaining_ -= bytes;return result;
}
AllocateFallback
用于为Arena申请新的内存块。首先,AllocateFallback会判断bytes是否大于1024。如果大于1024,AllocateFallback会调用AllocateNewBlock
申请一个大小为bytes的内存块并返回,Arena会继续使用当前内存块进行下一次的内存分配。弱国小于或等于1024,则AllocateFallback会调用AllocateNewBlock申请一个大小为4KB的内存块并在新的内存块上分配内存。原来的内存块中未分配的空间则被浪费。
我们再来看一下AllocateNewBlock
的源码:
char* Arena::AllocateNewBlock(size_t block_bytes) {char* result = new char[block_bytes];blocks_.push_back(result);memory_usage_.fetch_add(block_bytes + sizeof(char*),std::memory_order_relaxed);return result;
}
AllocateNewBlock的代码很简单,它首先使用new[]
申请block_bytes
个字节的内存,然后将这个内存块加入block_
中(Arena在析构时会释放掉block_中所有的内存),再记录一下内存的使用量就可以了。
AllocateAligned的实现
char* Arena::AllocateAligned(size_t bytes) {const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;static_assert((align & (align - 1)) == 0,"Pointer size should be a power of 2");size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - 1);size_t slop = (current_mod == 0 ? 0 : align - current_mod);size_t needed = bytes + slop;char* result;if (needed <= alloc_bytes_remaining_) {result = alloc_ptr_ + slop;alloc_ptr_ += needed;alloc_bytes_remaining_ -= needed;} else {result = AllocateFallback(bytes);}assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);return result;
}
AllocateAligned
以sizeof(void*)对齐内存(如果sizeof(void*)大于8的话),所以在64位机上它使用8字节对齐。变量slop
的值是对齐到8个字节所需要的字节数。slop加上bytes就是分配这段内存所需要的字节数。接下来的流程就跟Allocate
一样了。
总结
Arena的主要内容基本上就分析完了。虽然Arena的设计简单,却又十分巧妙,成功解决了内存碎片以及频繁调用new和delete的问题。博主接下来的一个小项目需要内存池来分配空间存储一些小的字符串,正好可以实现一个类似于Arena的内存池试试效果。
参考
- LevelDB Github仓库
- leveldb 笔记三:Arena 内存管理
LevelDB Arena源码分析相关推荐
- Leveldb源码分析--1
[前言:看了一点oceanbase,没有意志力继续坚持下去了,暂时就此中断,基本上算把master看完了,比较重要的update server和merge server代码却没有细看.中间又陆续研究了 ...
- ptmalloc源码分析 - 多线程争抢竞技场Arena的实现(04)
目录 一.为何要引入Arena竞技场概念 二.主分配区和非主分配区的数据结构 三.获取分配区主函数arena_get 四.首次申请分配区的核心函数arena_get2 1.get_free_list ...
- Python3.5源码分析-内存管理
Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的内存管理概述 python提供了对内存的垃圾收 ...
- 《Ceph源码分析》——第1章,第5节RADOS
本节书摘来自华章出版社<Ceph源码分析>一书中的第1章,第1.5节RADOS,作者常涛,更多章节内容可以访问云栖社区"华章计算机"公众号查看 1.5 RADOS RA ...
- glibc-2.23学习笔记(二)—— free部分源码分析
glibc-2.23学习笔记(二)-- free部分源码分析 _libc_free _int_free 函数定义 局部变量 start fast bins部分 unsorted bins部分 mmap ...
- glibc-2.23学习笔记(一)—— malloc部分源码分析
glibc-2.23学习笔记(一)-- malloc部分源码分析 搭建Glibc源码调试环境 1.下载并解压glibc源码 2.配置gdb 3.编译测试程序 第一次调用 源码分析 __libc_mal ...
- NEO从源码分析看共识协议
2019独角兽企业重金招聘Python工程师标准>>> 0x00 概论 不同于比特币使用的工作量证明(PoW)来实现共识,NEO提出了DBFT共识算法.DBFT改良自股权证明算法(P ...
- 【作者面对面问答】包邮送《Redis 5设计与源码分析》5本
墨墨导读:本文节选自<Redis 5设计与源码分析>,主要为读者分析Redis高性能内幕,重点从源码层次讲解了Redis事件模型,网络IO事件重在使用IO复用模型,时间事件重在限制最大执行 ...
- caffe.proto源码分析
一什么是protocol buffer 二caffeproto中的几个重要数据类型 三caffeproto源码 分析caffe源码,看首先看caffe.proto,是明智的选择.好吧,我不是创造者,只 ...
最新文章
- 能源枯竭?在能源互联网时代不存在!
- Vs code如何快速生成Verilog例化模板
- python类介绍_python类介绍
- Mysql翻转字符串reverse
- 【开发工具】盘点IDEA那些超级实用插件
- 【汇编语言】上机实验 win7/8/10 64位系统 进入32位DOS模式 实现dubug/edit/masm/link功能
- QC与IE8 、WINDOWS 7 兼容问题的解决方案
- java数据加密解密代码_java使用RSA加密方式实现数据加密解密的代码
- python mobilenetssd android_MobileNetV2-SSDLite运行
- python安装caffe_caffe中安装python
- Flex builder3相关
- appscan如何扫描移动应用APP
- html怎么设置字体的背景颜色,html怎样设置字体的背景颜色?
- 前端常用PS技巧总结之将图片背景透明化
- Windows系统桌面快捷方式图标去除小箭头
- 论文阅读_Reducing Test Cases with Attention Mechanism of Neural Networks
- 写代码写文章勿有功利心
- 后羿 05 ‖ 九婴
- jsBlob数据转为图片
- 基础编程题目集-7-32 说反话-加强版 (20分)