欢迎访问我的个人博客

https://vincillau.github.io/

文章目录

    • 欢迎访问我的个人博客
    • https://vincillau.github.io/
  • LevelDB Arena源码分析
    • 什么是Arena
    • Arena的实现思路
    • Arena的实现
      • Allocate的实现
    • AllocateAligned的实现
    • 总结
    • 参考

LevelDB Arena源码分析

什么是Arena

ArenaLevelDB中实现的一个简易的内存池。因为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对象使用的内存总量。我们重点看一下AllocateAllocateAligned的实现:

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的内存池试试效果。

参考

  1. LevelDB Github仓库
  2. leveldb 笔记三:Arena 内存管理

LevelDB Arena源码分析相关推荐

  1. Leveldb源码分析--1

    [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂时就此中断,基本上算把master看完了,比较重要的update server和merge server代码却没有细看.中间又陆续研究了 ...

  2. ptmalloc源码分析 - 多线程争抢竞技场Arena的实现(04)

    目录 一.为何要引入Arena竞技场概念 二.主分配区和非主分配区的数据结构 三.获取分配区主函数arena_get 四.首次申请分配区的核心函数arena_get2 1.get_free_list ...

  3. Python3.5源码分析-内存管理

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的内存管理概述 python提供了对内存的垃圾收 ...

  4. 《Ceph源码分析》——第1章,第5节RADOS

    本节书摘来自华章出版社<Ceph源码分析>一书中的第1章,第1.5节RADOS,作者常涛,更多章节内容可以访问云栖社区"华章计算机"公众号查看 1.5 RADOS RA ...

  5. glibc-2.23学习笔记(二)—— free部分源码分析

    glibc-2.23学习笔记(二)-- free部分源码分析 _libc_free _int_free 函数定义 局部变量 start fast bins部分 unsorted bins部分 mmap ...

  6. glibc-2.23学习笔记(一)—— malloc部分源码分析

    glibc-2.23学习笔记(一)-- malloc部分源码分析 搭建Glibc源码调试环境 1.下载并解压glibc源码 2.配置gdb 3.编译测试程序 第一次调用 源码分析 __libc_mal ...

  7. NEO从源码分析看共识协议

    2019独角兽企业重金招聘Python工程师标准>>> 0x00 概论 不同于比特币使用的工作量证明(PoW)来实现共识,NEO提出了DBFT共识算法.DBFT改良自股权证明算法(P ...

  8. 【作者面对面问答】包邮送《Redis 5设计与源码分析》5本

    墨墨导读:本文节选自<Redis 5设计与源码分析>,主要为读者分析Redis高性能内幕,重点从源码层次讲解了Redis事件模型,网络IO事件重在使用IO复用模型,时间事件重在限制最大执行 ...

  9. caffe.proto源码分析

    一什么是protocol buffer 二caffeproto中的几个重要数据类型 三caffeproto源码 分析caffe源码,看首先看caffe.proto,是明智的选择.好吧,我不是创造者,只 ...

最新文章

  1. 能源枯竭?在能源互联网时代不存在!
  2. Vs code如何快速生成Verilog例化模板
  3. python类介绍_python类介绍
  4. Mysql翻转字符串reverse
  5. 【开发工具】盘点IDEA那些超级实用插件
  6. 【汇编语言】上机实验 win7/8/10 64位系统 进入32位DOS模式 实现dubug/edit/masm/link功能
  7. QC与IE8 、WINDOWS 7 兼容问题的解决方案
  8. java数据加密解密代码_java使用RSA加密方式实现数据加密解密的代码
  9. python mobilenetssd android_MobileNetV2-SSDLite运行
  10. python安装caffe_caffe中安装python
  11. Flex builder3相关
  12. appscan如何扫描移动应用APP
  13. html怎么设置字体的背景颜色,html怎样设置字体的背景颜色?
  14. 前端常用PS技巧总结之将图片背景透明化
  15. Windows系统桌面快捷方式图标去除小箭头
  16. 论文阅读_Reducing Test Cases with Attention Mechanism of Neural Networks
  17. 写代码写文章勿有功利心
  18. 后羿 05 ‖ 九婴
  19. jsBlob数据转为图片
  20. 基础编程题目集-7-32 说反话-加强版 (20分)

热门文章

  1. 阿里云企业邮箱代理商:foxmal邮件发送RCPT错误怎么办?
  2. 自动驾驶(四十七)---------超声波雷达简介
  3. 英语总结系列(二十三):Baby上海一月游
  4. php 星盘代码,爱星盘SDK开发接口说明
  5. 库卡机器人编程权限_库卡机器人编程之BAS程序
  6. [Office] 设置段落标题边框
  7. 傅里叶级数展开及系数项求解
  8. MVP架构开发的鼠绘漫画客户端
  9. Chrome 76 新特性
  10. Redis学习之hgetall