LevelDB(v1.3) 源码阅读之 Arena(内存管理器)
LevelDB(v1.3) 源码阅读系列使用 LevelDB v1.3 版本的代码,可以通过如下方式下载并切换到 v1.3 版本的代码:
$ git clone https://github.com/google/leveldb.git
$ cd leveldb
$ git checkout -b v1.3 v1.3
本文涉及的代码文件为:
util/arena.h
util/arena.cc
一、简介
Arena 是一个简单的内存分配器。通过一个 Arena 对象,可以进行一些内存分配操作。Arena 对象会将所有分配的内存都记录在案,在 Arena 对象析构的时候一起释放所有记录在案的已分配内存。
Arena 默认分配的内存大小是 4096KB,也就是 4K。每当有一个内存分配请求分配 X Bytes 的内存的时候,Arena 的分配原则如下:
- 如果当前分配的内存块中剩余的内存能够满足该分配请求,也就是说大于等于 X,那么就在当前的内存块中分配 X Bytes 给用户,否则:
- 如果 X 大于默认的内存块大小的 25%,也就是说 X > 1KB,那么就单独分配大小恰好是 X Bytes 的内存给用户,否则:
- 新分配一块大小为 4KB 的内存,在里面分配 X Bytes 给用户(也就是说,上一块内存中可能有些内存没有被分配给用户就被 Arena 抛弃了,这里会造成小于 1KB 的内存浪费)
这里需要注意两个数字:4KB 和 1/4。
- 1/4 是一个经验值:是减少内存碎片和提高内存分配器性能的一个 trade off;
- Linux 内核的 page size 为 4KB
可以通过如下命令查看 page size:
$ getconf PAGESIZE
此外,Arena 还提供了分配地址对齐的内存块的接口:调用这个接口,用户会得到他想要的 X Bytes 的内存块,该内存块的起始地址是 8 的倍数:形如 0x_______0 和 0x_______8,如果在分配内存的时候发现当前的地址并不满足这种形式,那就多分配点内存用来填补不够的 Byte(最多浪费 7 个 Bytes),之后的分配原则就和上面讲的三大原则一致了。这样的内存块的带来的优势是 CPU 寻址速度快:
- 在 32 位机器上,地址总线是 32 bits 的,指针的 size 为 4 Bytes;64 位机器上,地址总线是 64 bits 的,指针的 size 为 8 Bytes。
- CPU 取内存中的数据并不是一个 Byte 一个 Byte 的取,在 32 位机器上,一次取的数量是 4 Bytes,64 位机器上一次取 8 Bytes。
值得一提的是,每次我们从堆中 new 一块内存的时候,操作系统返回的该内存块的首地址是一定能被 8 整除(地址对齐)的。
关于内存分配部分,这里都讲的差不多了,代码我就贴一点点吧。
成员变量
// Allocation state
char* alloc_ptr_; // 指向当前内存块中剩余未分配内存块的首部
size_t alloc_bytes_remaining_; // 保存当前内存块中剩余的可分配出去的内存大小
// Array of new[] allocated memory blocks
std::vector<char*> blocks_; // 保存所有分配的内存块
// Bytes of memory in blocks allocated so far
size_t blocks_memory_; // 保存该 Arena 对象总共申请内存的大小
对外接口
Arena();
~Arena();
char* Allocate(size_t bytes);
char* AllocateAligned(size_t bytes);
size_t MemoryUsage() const {
return blocks_memory_ + blocks_.capacity() * sizeof(char*);
}
其中,Allocate(size_t) 用来分配非地址对齐的内存,AllocateAligned(size_t) 用来分配地址对齐的内存
值得一提的是,Arena 对象不允许拷贝操作,为了完成这个,它把以一个 Arena 对象作为参数的构造函数声明称私有的,并重载 '=' 为私有
关于分配内存的三条原则,可以在 Allocate(size_t) 的实现和一个私有成员函数 char* AllocateFallback(size_t bytes);
中查看:
static const int kBlockSize = 4096;
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);
}
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.
char* result = AllocateNewBlock(bytes);
return result;
}
// We waste the remaining space in the current block.
alloc_ptr_ = AllocateNewBlock(kBlockSize);
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];
blocks_memory_ += block_bytes;
blocks_.push_back(result);
return result;
}
代码中的注释已经很好的解释其行为了,我就不多嘴了。
LevelDB(v1.3) 源码阅读之 Arena(内存管理器)的更多相关文章
- LevelDB(v1.3) 源码阅读之 Slice
LevelDB(v1.3) 源码阅读系列使用 LevelDB v1.3 版本的代码,可以通过如下方式下载并切换到 v1.3 版本的代码: $ git clone https://github.com/ ...
- linux0.11内核源码剖析:第一篇 内存管理、memory.c【转】
转自:http://www.cnblogs.com/v-July-v/archive/2011/01/06/1983695.html linux0.11内核源码剖析第一篇:memory.c July ...
- 源码解读·RT-Thread小内存管理算法分析
这篇文章最初发布在RT-Thread官方论坛中,最近准备整理放到博客中来让更多人一起探讨学习. 2012年9月28日星期五 前言: 母语能力有限 概述: 这篇文字和大家分享一下今晚对RT-Thread ...
- [日常] gocron源码阅读-使用go mod管理依赖源码启动gocron
从 Go1.11 开始,golang 官方支持了新的依赖管理工具go modgo mod download: 下载依赖的 module 到本地 cachego mod edit: 编辑 go.modg ...
- CoreCLR源码探索(四) GC内存收集器的内部实现 分析篇
在这篇中我将讲述GC Collector内部的实现, 这是CoreCLR中除了JIT以外最复杂部分,下面一些概念目前尚未有公开的文档和书籍讲到. 为了分析这部分我花了一个多月的时间,期间也多次向Cor ...
- CoreCLR源码探索(五) GC内存收集器的内部实现 调试篇
在上一篇中我分析了CoreCLR中GC的内部处理, 在这一篇我将使用LLDB实际跟踪CoreCLR中GC,关于如何使用LLDB调试CoreCLR的介绍可以看: 微软官方的文档,地址 我在第3篇中的介绍 ...
- WorldWind源码剖析系列:图层管理器按钮类LayerManagerButton和菜单条类MenuBar
WorldWindow用户定制控件类中所包含的的可视化子控件主要有:图层管理器按钮类LayerManagerButton和菜单条类MenuBar.BmngLoader类中所包含的的可视化子控件主要有: ...
- kubernetes源码阅读及编译
kubernetes源码阅读 工欲善其事,必先利其器.在阅读kubernetes源码时,我也先后使用过多个IDE,最终还是停留在IDEA上. 我惯用的是pycharm(IDEA的python IDE版 ...
- JDK源码阅读(一):Object源码分析
最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...
随机推荐
- NPM小结
nodejs的出现,可以算是前端里程碑式的一个事件,它让前端攻城狮们摆脱了浏览器的束缚,踏上了一个更加宽广的舞台.前端的可能性,从此更加具有想象空间. 随着一系列基于nodes的应用/工具的出现,工作 ...
- Atitit.auto complete 自动完成控件的实现总结
Atitit.auto complete 自动完成控件的实现总结 1. 框架选型 1 2. 自动完成控件的ioc设置 1 3. Liger 自动完成控件问题 1 4. 官网上的code有问题,不能 ...
- Javascript设置广告和时间表和数组的学习
<html> <head> <meta charset="utf-8"> <title></title> </he ...
- 一些新的web性能优化技术
1.IconFont:图标字体,这是近年来新流行的一种以字体代替图片的技术.它可以适应任何分辨率而不会出现图片模糊问题,与图片相比它具有更小的容量,更高的灵活性(像字体一样可以设置图标大小.颜色.透明 ...
- Python面试题(二)
打印九九乘法表 思路:利用字符串的连接,梯形输出结果 >>> def st(num): ... l = [] ... for x in xrange(1, num + 1): ... ...
- Activiti 查询最新版本的流程定义
package com.mycom.processDefinition; import java.io.File; import java.io.IOException; import java.io ...
- 磁盘阵列RAID原理、种类及性能优缺点对比
磁盘阵列(Redundant Arrays of Independent Disks,RAID) 1. 存储的数据一定分片: 2. 分基于软件的软RAID(如mdadm)和基于硬件的硬RAID(如RA ...
- int跟byte[]数组互转的方法,整数 + 浮点型
整数: int转byte数组 public static byte[] intToBytes2(int n){ ]; ;i < ;i++) { b[i]=(-i*)); } return b; ...
- 理解TCP为什么需要进行三次握手(白话)
原文地址:http://www.cnblogs.com/yuilin/archive/2012/11/05/2755298.html 首先简单介绍一下TCP三次握手 在TCP/IP协议中,TCP协议提 ...
- Kernel Function--核函数收集
转自 http://www.zhizhihu.com/html/y2010/2292.html Kernel Functions Below is a list of some kernel func ...