背景
我们都知道,对于一个高性能的服务器端程序来说,内存的使用非常重要。C++提供了new/delete来管理内存的申请和释放,但是对于小对象来说,直接使用new/delete代价比较大,要付出额外的空间和时间,性价比不高。
另外,我们也要避免多次的申请和释放引起的内存碎片。一旦碎片到达一定程度,即使剩余内存总量够用,但由于缺乏足够的连续空闲空间,导致内存不够用的假象。
c++ STL为了避免内存碎片,实现一个复杂的内存池,leveldb中则没有那么复杂,只是实现了一个"一次性"内存池Arena。
在leveldb里面,并不是所有的地方都使用了这个内存池,主要是memtable使用,主要是用于临时存放用户的更新数据,由于更新的数据可能很小,所以这里使用内存池就很合适。

原理
为了避免小对象的频繁分配,需要减少对new的调用,最简单的做法就是申请大块的内存,多次分给客户。
leveldb用一个vector<char *>来保存所有的内存分配记录,默认每次申请4k的内存,记录下剩余指针和剩余内存字节数,每当有新的申请,如果当前剩余的字节能满足需要,则直接返回给用户,如果不能,对于超过1k的请求,直接new返回,小于1K的请求,则申请一个新的4k块,从中分配一部分给用户。
但是这样的一个问题就是当前块剩余的部分就浪费了,改进的方法,针对每个block都记录剩余字节,这样就需要遍历来查找合适的block,要付出一些性能的代价。google的做法是浪费就浪费吧:-)
至于释放,需要释放整个内存池来释放所占内存,这个和leveldb的需求有关,memtable不需要释放单次内存,flush到硬盘后整个memtable销毁。

Arena.h
//z 2014-06-05 10:48:50 L.209'47470 BG57IV3 T1840949363.K.F1370514324[T6,L108,R4,V118]

  1. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. #ifndef STORAGE_LEVELDB_UTIL_ARENA_H_
  5. #define STORAGE_LEVELDB_UTIL_ARENA_H_
  6. #include <cstddef>
  7. #include <vector>
  8. #include <assert.h>
  9. #include <stdint.h>
  10. namespace leveldb
  11. {
  12. /*//z
  13. 思路:
  14. 先在上一次分配的内存块中查找是否剩余空间能否容纳当前申请的空间;如果足以容纳,直接使用剩余空间
  15. 否则视其大小重新分配一块空间:如果大于1024,直接分配一块新空间,大小与申请空间大小同
  16. 如果小于1024,说明上一块空间剩余空间小于1024,那么重新分配一块4096大小的空间,使用该空间来存放待申请的空间
  17. */
  18. class Arena
  19. {
  20. public:
  21. Arena();
  22. ~Arena();
  23. // Return a pointer to a newly allocated memory block of "bytes" bytes.
  24. char* Allocate(size_t bytes);
  25. // Allocate memory with the normal alignment guarantees provided by malloc
  26. char* AllocateAligned(size_t bytes);
  27. // Returns an estimate of the total memory usage of data allocated
  28. // by the arena (including space allocated but not yet used for user
  29. // allocations).
  30. size_t MemoryUsage() const
  31. {
  32. return blocks_memory_ + blocks_.capacity() * sizeof(char*);
  33. }
  34. private:
  35. char* AllocateFallback(size_t bytes);
  36. char* AllocateNewBlock(size_t block_bytes);
  37. // Allocation state
  38. char* alloc_ptr_;
  39. size_t alloc_bytes_remaining_;
  40. // Array of new[] allocated memory blocks
  41. std::vector<char*> blocks_;
  42. // Bytes of memory in blocks allocated so far
  43. size_t blocks_memory_;
  44. // No copying allowed
  45. Arena(const Arena&);
  46. void operator=(const Arena&);
  47. };
  48. inline char* Arena::Allocate(size_t bytes)
  49. {
  50. // The semantics of what to return are a bit messy if we allow
  51. // 0-byte allocations, so we disallow them here (we don't need
  52. // them for our internal use).
  53. assert(bytes > 0);
  54. //z 在一块直接分配好的内存上移动一下指针即可;事实上可能会存在很多的浪费。
  55. if (bytes <= alloc_bytes_remaining_)
  56. {
  57. char* result = alloc_ptr_;
  58. alloc_ptr_ += bytes;
  59. alloc_bytes_remaining_ -= bytes;
  60. return result;
  61. }
  62. //z 如果剩余控件不足与容纳,那么根据bytes大小决定是否重新分配一块标准大小的内存(4096),或者要是bytes大于1024,直接
  63. //z 分配其大小的内存,原标准块内存仍旧有用。
  64. //z 如果小于 1024 ,说明原标准块剩余内存不足以容纳1024,新分配一块标准块内存,用此来为bytes分配对应内存。
  65. return AllocateFallback(bytes);
  66. }
  67. }
  68. #endif  // STORAGE_LEVELDB_UTIL_ARENA_H_

//z 2014-06-05 10:48:50 L.209'47470 BG57IV3 T1840949363.K.F1370514324[T6,L108,R4,V118]
arena.cc

  1. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. #include "util/arena.h"
  5. #include <assert.h>
  6. namespace leveldb {
  7. //z 常量变量名k开头
  8. static const int kBlockSize = 4096;
  9. //z 初始化
  10. Arena::Arena() {
  11. blocks_memory_ = 0;
  12. alloc_ptr_ = NULL;  // First allocation will allocate a block
  13. alloc_bytes_remaining_ = 0;
  14. }
  15. Arena::~Arena() {
  16. //z 删除申请的内存
  17. for (size_t i = 0; i < blocks_.size(); i++) {
  18. delete[] blocks_[i];
  19. }
  20. }
  21. char* Arena::AllocateFallback(size_t bytes) {
  22. //z 如果申请的bytes > 1024
  23. if (bytes > kBlockSize / 4) {
  24. // Object is more than a quarter of our block size.  Allocate it separately
  25. // to avoid wasting too much space in leftover bytes.
  26. char* result = AllocateNewBlock(bytes);
  27. return result;
  28. }
  29. // We waste the remaining space in the current block.
  30. //z 不大于1024时,分配一块标准大小的内存
  31. alloc_ptr_ = AllocateNewBlock(kBlockSize);
  32. alloc_bytes_remaining_ = kBlockSize;
  33. //z 指定返回的位置
  34. char* result = alloc_ptr_;
  35. //z 移动指针位置至空闲内存开始的地方
  36. alloc_ptr_ += bytes;
  37. //z 记录还剩下多少内存
  38. alloc_bytes_remaining_ -= bytes;
  39. return result;
  40. }
  41. char* Arena::AllocateAligned(size_t bytes) {
  42. //z 这个值是固定的,不用每次都求一次?但是代价非常小,求一次也无所为?
  43. const int align = sizeof(void*);    // We'll align to pointer size
  44. assert((align & (align-1)) == 0);   // Pointer size should be a power of 2
  45. //z 求的其余数
  46. size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1);
  47. size_t slop = (current_mod == 0 ? 0 : align - current_mod);
  48. size_t needed = bytes + slop;
  49. char* result;
  50. //z 如果剩余的空间足以容纳所需要的内存
  51. if (needed <= alloc_bytes_remaining_) {
  52. //z 对齐返回地址
  53. result = alloc_ptr_ + slop;
  54. alloc_ptr_ += needed;
  55. alloc_bytes_remaining_ -= needed;
  56. } else {
  57. // AllocateFallback always returned aligned memory
  58. //z 否则直接分配一块新的内存
  59. //z 在这种情况下这块内存可能很小
  60. result = AllocateFallback(bytes);
  61. }
  62. //z 确保返回地址是对齐的
  63. assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);
  64. return result;
  65. }
  66. //z 在不小于一个page的时候,直接采用这种方式
  67. char* Arena::AllocateNewBlock(size_t block_bytes) {
  68. char* result = new char[block_bytes];
  69. blocks_memory_ += block_bytes;
  70. blocks_.push_back(result);
  71. return result;
  72. }
  73. }

//z 2014-06-05 10:48:50 L.209'47470 BG57IV3 T1840949363.K.F1370514324[T6,L108,R4,V118]

leveldb Arena的更多相关文章

  1. leveldb Arena源码分析

    前言 对于一个高性能的服务器程序来说,内存的使用非常重要.C++提供new/delete来管理内存的申请和释放,但是对于小对象来说,直接使用new/delete代价比较大,要付出额外的空间和时间,性价 ...

  2. LevelDB(v1.3) 源码阅读之 Arena(内存管理器)

    LevelDB(v1.3) 源码阅读系列使用 LevelDB v1.3 版本的代码,可以通过如下方式下载并切换到 v1.3 版本的代码: $ git clone https://github.com/ ...

  3. leveldb分析——Arena内存管理

    leveldb中实现了一个简单的内存管理工具Arena,其基本思想为:先预先向系统申请一块内存,此后需要申请内存时,直接到预先分配的内存中申请. 那么这样做的目的是什么呢? (1)避免了频率地进行ma ...

  4. LevelDB源码分析之:arena内存管理

    一.原理 arena是LevelDB内部实现的内存池. 我们知道,对于一个高性能的服务器端程序来说,内存的使用非常重要.C++提供了new/delete来管理内存的申请和释放,但是对于小对象来说,直接 ...

  5. leveldb源码分析之内存池Arena

    转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...

  6. LevelDB 源码解析之 Arena

    GitHub: https://github.com/storagezhang Emai: debugzhang@163.com 华为云社区: https://bbs.huaweicloud.com/ ...

  7. LevelDB学习笔记 (3): 长文解析memtable、跳表和内存池Arena

    LevelDB学习笔记 (3): 长文解析memtable.跳表和内存池Arena 1. MemTable的基本信息 我们前面说过leveldb的所有数据都会先写入memtable中,在leveldb ...

  8. LevelDB源码剖析

    LevelDB的公共部件并不复杂,但为了更好的理解其各个核心模块的实现,此处挑几个关键的部件先行备忘. Arena(内存领地) Arena类用于内存管理,其存在的价值在于: 提高程序性能,减少Heap ...

  9. 【转】Leveldb源码分析——1

    先来看看Leveldb的基本框架,几大关键组件,如图1-1所示. Leveldb是一种基于operation log的文件系统,是Log-Structured-Merge Tree的典型实现.LSM源 ...

随机推荐

  1. 关于enter事件的触发

    如果您使用了antd的Button组件,那么恭喜已经封装好了,只要加上htmlType='submit', 如果没有使用其他框架,使用onPress或者onKeydown事件,判断e.keycode ...

  2. Python 操作 MySQL 数据库Ⅱ

    数据库连接 连接数据库前,请先确认以下事项: 您已经创建了数据库 TESTDB. 在TESTDB数据库中您已经创建了表 EMPLOYEE EMPLOYEE表字段为 FIRST_NAME, LAST_N ...

  3. git的安装教程

    团队开发的时候常要用到git来对代码进行管理,平时开发的时候也需要使用git来做一些事情,如给vue项目进行代码编译等等. 那下面我们开始 一.安装和配置环境 首先下载git安装包(去官网下载,htt ...

  4. jmeter-响应有中文时,显示乱码

    Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

  5. Nowcoder farm ( 树状数组、二维前缀和、二维偏序 )

    题目链接 分析 : 最简单的想法当然就是去模拟 直接对每个施肥料的操作进行模拟.然后计算贡献 但是这显然会超时.这题需要换一个思维 对于一个土地(也就是二维平面上的一个点)的种类是 T' 如果它被操作 ...

  6. CodeForces 788B--Weird journey

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Description Little ...

  7. mysql 链接数满了的错误 ERROR 1040 (HY000): Too many connections

    mysql 链接数满了的错误 ERROR 1040 (HY000): Too many connections 第一种处理方式: ./mysql -u root -p 登录成功后执行以下语句查询当前的 ...

  8. Android动态广播的注册与销毁

    一个内部类:BroadcastReceiver的子类,并定义收到广播之后的操作: class LockScreenBroadcastReceiver extends BroadcastReceiver ...

  9. 查询redis中没有设置过期时间的key

    #!/bin/sh ## 该脚本用来查询redis集群中,哪些key是没有设置过期时间,对应只需要修改redis的其中一个实例的 host和port ## 脚本会自动识别出该集群的所有实例,并查出对应 ...

  10. 【转】C/C++ 引用作为函数的返回值

    转自:https://blog.csdn.net/weixin_40539125/article/details/81410008 这篇文章写的很好: 语法:类型 &函数名(形参列表){ 函数 ...