LevelDB内存池Arena

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LevelDB内存池Arena相关的知识,希望对你有一定的参考价值。

LevelDB内存池Arena

util/arena.h

namespace leveldb {

class Arena {
 public:
  Arena();

  Arena(const Arena&) = delete;
  Arena& operator=(const Arena&) = delete;

  ~Arena();

  // Return a pointer to a newly allocated memory block of "bytes" bytes.
    //不保证内存对齐的情况下分配内存
  char* Allocate(size_t bytes);

  // Allocate memory with the normal alignment guarantees provided by malloc.
    //在保证内存对齐的情况下分配内存
  char* AllocateAligned(size_t bytes);

  // Returns an estimate of the total memory usage of data allocated
  // by the arena.
  size_t MemoryUsage() const {
      //就是返回原子变量memory_usage_中的值,在这里就是返回这个size_t值,也就是arena用了多少内存
    return memory_usage_.load(std::memory_order_relaxed);
  }

 private:
    //用于向操作系统申请内存,只不过按照需求bytes和kBlockSize/4的大小,分为两种情况分配,具体还要调用了AllocateNewBlock()
  char* AllocateFallback(size_t bytes);
    //用于向操作系统申请block_bytes这么多内存
  char* AllocateNewBlock(size_t block_bytes);

  // Allocation state
    //指向了arena中空闲内存的地址的指针
  char* alloc_ptr_;
    //当前arena中还有多少内存可供用户申请
  size_t alloc_bytes_remaining_;

  // Array of new[] allocated memory blocks
    //用来保存arena向操作系统申请的内存
  std::vector<char*> blocks_;

  // Total memory usage of the arena.
  //
  // TODO(costan): This member is accessed via atomics, but the others are
  //               accessed without any locking. Is this OK?
    //用来保存arena一共用了多少内存,其实就是blocks_中每个block的大小之和再加上指向每个block的指针的大小
  std::atomic<size_t> memory_usage_;
};

//不保证内存对齐的情况下分配内存
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_) {
      //arena中剩余内存满足要求就直接分配
    char* result = alloc_ptr_;
      //指向arena中的空闲内存的指针向后推
    alloc_ptr_ += bytes;
      //剩余量减少
    alloc_bytes_remaining_ -= bytes;
    return result;
  }
    //不满足要求,就新申请内存
  return AllocateFallback(bytes);
}

}  // namespace leveldb

util/arena.cc

#include "util/arena.h"

namespace leveldb {

static const int kBlockSize = 4096;

Arena::Arena()
    : alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {}

Arena::~Arena() {
  for (size_t i = 0; i < blocks_.size(); i++) {
    delete[] blocks_[i];
  }
}

char* Arena::AllocateFallback(size_t bytes) {
    //当向arena申请的内存超过kBlockSize的1/4的时候,就直接向操作系统申请byte的内存来使用,而arena之前的申请的内存没有变化,所以没有造成浪费
  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.
    //此时就造成了浪费,参数bytes没有超过kBlockSize的1/4
    //arena直接向内存申请kBlockSzie大小的内存
  alloc_ptr_ = AllocateNewBlock(kBlockSize);
    //arena中剩余的没有使用的内存就是arena新申请的内存,就算之前arena中还有剩余的内存,也不再使用了,所以造成了浪费
  alloc_bytes_remaining_ = kBlockSize;
	//将返回的内存指针指向这个新申请的内存,用户从这里开始使用
  char* result = alloc_ptr_;
    //下一次用户向arena申请内存时,就从alloc_ptr_开始
  alloc_ptr_ += bytes;
    //arena中剩余的内存就是当前申请了bytes内存后剩余的
  alloc_bytes_remaining_ -= bytes;
  return result;
}

//对齐,在保证内存对齐的情况下分配内存
char* Arena::AllocateAligned(size_t bytes) {
    //用指针的长度来对齐,如果长度小于或者等于8,那么就按照8字节对齐
    //8&7=0
  const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;
  static_assert((align & (align - 1)) == 0,
                "Pointer size should be a power of 2");
    //uintptr_t就是unsigned long,reinterpret可以用来内置类型的强制类型转换,这个可以用来计算指针alloc_ptr_里面保存的地址相对于对齐来说的偏移量
    //例如按照8字节对齐,那么current_mod就是低7位的值。相当于mod 8
  size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - 1);
    //如果已经对齐,那么差值就是0,否则就是差值
  size_t slop = (current_mod == 0 ? 0 : align - current_mod);
    //需要的内存空间就是申请的bytes+用于对齐的slop
  size_t needed = bytes + slop;
  char* result;
  if (needed <= alloc_bytes_remaining_) {
      //如果arena中还有剩余的内存满足当前申请的内存,那么就不用申请新内存
      //返回对齐的地址
    result = alloc_ptr_ + slop;
      //将arena中已经分配内存的指针向后移动
    alloc_ptr_ += needed;
      //更新arena中剩余内存量
    alloc_bytes_remaining_ -= needed;
  } else {
    // AllocateFallback always returned aligned memory
      //arena中剩余内存不够,那么就新分配
    result = AllocateFallback(bytes);
  }
    //再次检验是否对齐
  assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);
  return result;
}

char* Arena::AllocateNewBlock(size_t block_bytes) {
  char* result = new char[block_bytes];
    //申请一段内存,并且将他们保存到blocks_这个vector中,可供将来使用
  blocks_.push_back(result);
    //将原子变量memory_usage_的值原子的加上(block_bytes+sizeof(char *)),函数的返回值是memory_usage_加上参数之前的值
    //主要是记录当前arena使用的内存的量
    //第二个参数有以下几种:
    //memory_order_relaxed	不对执行顺序作保证,只保证操作是原子的
    //memory_order_acquire	在本线程中,所有后续读操作必须在本原子操作完成之后执行
    //memory_order_release	在本线程中,所有之前的写操作完成之后才能执行本条原子操作
    //memory_order_acq_rel	同时包含memory_order_acquire和memory_order_release
    //memory_order_consume	在本线程中,所有后续有关本原子变量的操作必须在本条原子操作完成之后才能执行
    //memory_order_seq_cst	全部存取按顺序进行
  memory_usage_.fetch_add(block_bytes + sizeof(char*),
                          std::memory_order_relaxed);
  return result;
}

}  // namespace leveldb

以上是关于LevelDB内存池Arena的主要内容,如果未能解决你的问题,请参考以下文章

leveldb登山之路——arena

LevelDB 源码剖析公共基础:内存管理数值编码Env家族文件操作

LevelDB 源码剖析公共基础:内存管理数值编码Env家族文件操作

LevelDB 源码剖析公共基础:内存管理数值编码Env家族文件操作

LevelDB源码阅读Arena

LevelDB源码阅读Arena