Leveldb学习之路1----内存管理

源代码路径

util/arena.h
util/arena.cc

Arena简介

数据库对内存的分配释放要求非常严格,频繁的调用malloc/free或者new/delete开销非常大,他们频繁的向操作系统申请堆内存,这个过程涉及到用户态向内核态的切换,在内核态分配完空间后再切换到用户态。同时频繁调用malloc再free容易造成大量的内部碎片。因此我们需要自己构造一个缓冲区域:内存池。我们可以一次性申请一个较大的内存区域,后续的小的内存分配由用户态自己管理。Arena就是Leveldb根据自身特性实现的一个超简易版内存池。他的大致结构如下:
在这里插入图片描述

其中kBlockSize设置为4k。即一般情况下向操作系统申请内存时申请的大小为4k。Arena把每次申请的block都保存在blocks_数组里。
在用户向Arena申请内存流程如下:

  1. Arena会首先判断当前的block中剩余的空间是否满足,如果满足则直接进行分配。否则到2
  2. 为了减少内部碎片(Internal Fragmentation, IF)当申请的内存大于1k时,Arena会直接申请一个新的block供用户使用(如图中的blocks_中的3,4),当前block可以复用.否则转到3
  3. 如果小于1k,则当前block中的空闲内存会被当成内部碎片,Arena会申请一个新的block并切换在新的block中为后续的用户申请分配内存。如图中的blocks_ 0中的黄色即内部碎片。

源代码分析 Internal Fragmentation

数据结构

class Arena {
 private:
  char* AllocateFallback(size_t bytes);
  char* AllocateNewBlock(size_t block_bytes);

  // Allocation state
  char* alloc_ptr_;
  size_t alloc_bytes_remaining_;

  // Array of new[] allocated memory blocks
  std::vector<char*> blocks_;

  // Total memory usage of the arena.
  std::atomic<size_t> memory_usage_;
}

alloc_ptr 表示当前block中空闲内存的起始位置
alloc_bytes_remaining_ 表示当前block空闲内存大小
blocks_ 表示内存管理中已分配的block数组
memory_usage_ 表示总共使用的内存大小

内部方法

  1. AllocateNewBlock 的作用是直接申请一个新的block,且新的block大小等于用户所要申请的内存大小。
char* Arena::AllocateNewBlock(size_t block_bytes) {
  // 申请block_bytes大小的内存
  char* result = new char[block_bytes];
  // 将当前block放入blocks中
  blocks_.push_back(result);
  // 更新内存使用情况
  memory_usage_.fetch_add(block_bytes + sizeof(char*),
                          std::memory_order_relaxed);
  return result;
}
  1. AllocateFallback 用在当前block剩余空间不足时申请新的block并进行内存分配。如果要申请的空间过大,这里默认为一页的1/4,则直接分配一个新的block,且当前的block可以继续供后续分配使用。
static const int kBlockSize = 4096;
char* Arena::AllocateFallback(size_t bytes) {
  // 如果申请的空间大于1/4页,则直接调用AllocateNewBlock申请一块
  // 新的block,且当前block的剩余空间可在后续使用,减少内部碎片。
  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;
}

外部接口

  1. Allocate 用来申请内存。如果当前block空闲空间足够,则直接分配内存,否则调用内部AllocateFallback方法。block的申请由AllocateFallback方法负责。
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);
}
  1. AllocateAligned 申请内存并对齐。
char* Arena::AllocateAligned(size_t bytes) {
  const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;
  // 做过leetcode都知道:align & (align - 1)可以用来判断 align 是不是
  // 2的n次方。
  static_assert((align & (align - 1)) == 0,
                "Pointer size should be a power of 2");
  // (align - 1) 是2^n-1, 二进制全为1。这样求&位运算表示取模。
  size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - 1);
  // 计算需要填充的bit数slop。
  size_t slop = (current_mod == 0 ? 0 : align - current_mod);
  // 计算出经过填充后最终需要申请的内存大小
  size_t needed = bytes + slop;
  // 后续操作类似Allocate
  char* result;
  if (needed <= alloc_bytes_remaining_) {
    result = alloc_ptr_ + slop;
    alloc_ptr_ += needed;
    alloc_bytes_remaining_ -= needed;
  } else {
    // AllocateFallback always returned aligned memory
    result = AllocateFallback(bytes);
  }
  assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);
  return result;
}
  1. MemoryUsage 查询当前内存使用。
  size_t MemoryUsage() const {
    return memory_usage_.load(std::memory_order_relaxed);
  }

Arena总结

Arena是Leveldb的一个快速内存管理的实现。它只有申请内存操作,没有释放的操作,这和Leveldb的底层数据结构性质息息相关:只有插入操作,没有删除操作(Leveldb在删除key的时候也是以插入新数据的方式删除)。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用,你提到了pip show jsonpath的安装成功。根据这个引用,我可以确认jsonpath已经成功安装在你的Python环境中。 至于你提到的"windows python LevelDB安装 metadata-generation-failed",我没有找到相关的引用来回答这个问题。但是我可以给你一些建议来解决这个问题。 首先,确保你已经正确地安装了LevelDB以及与之相关的依赖包。你可以尝试使用pip install命令来安装LevelDB。如果出现metadata generation failed的错误,可能是由于网络问题或者软件包源的问题导致的。你可以尝试使用其他的软件包源或者使用代理来解决这个问题。 另外,你还可以尝试使用conda来安装LevelDB,它是一个流行的Python包管理器,可以管理Python包及其依赖项。你可以在Anaconda官方网站上找到更多关于conda的信息和安装指南。 最后,如果上述方法都没有解决你的问题,我建议你检查一下你的操作系统和Python版本是否与LevelDB兼容。有时候,特定的软件包可能只能在特定的操作系统或者Python版本上正常工作。 希望这些建议对你有帮助,如果你还有其他问题,请随时提问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python38安装jsonpath失败问题解决](https://blog.csdn.net/qq_27371025/article/details/125855179)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [The Log: What every software engineer should know about real-time data's unifying abstraction](https://blog.csdn.net/iloveu8780/article/details/80097101)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值