【leveldb】Arena(六)

我们知道频繁的向系统申请内存是很容易造成内存碎片的,如果申请内存很小则更严重。leveldb针对这种小内存的临时频繁申请使用了Arena来解决。
Arena只是粗粒度的内存管理,只用在了MemTableskiplist中,是作为一个局部临时对象来使用并未用在全局,Arena的内部实现是存在内存浪费的。

一、结构

在这里插入图片描述

二、源码
arena.h
namespace leveldb {

class Arena {
 public:
  Arena();

  <!禁止拷贝构造、拷贝赋值操作>
  Arena(const Arena&) = delete;
  Arena& operator=(const Arena&) = delete;

  ~Arena();

   <!申请bytes大小内存,并返回指向内存的指针>
  // Return a pointer to a newly allocated memory block of "bytes" bytes.
  char* Allocate(size_t bytes);
   
   <!申请内存对齐的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 {
    return memory_usage_.load(std::memory_order_relaxed);
  }

 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.
  //
  // TODO(costan): This member is accessed via atomics, but the others are
  //               accessed without any locking. Is this OK?
  std::atomic<size_t> memory_usage_;
};

<!申请内存实现,如果当前剩余的内存满足客端申请的就直接分配,
  如果满足不了,就调用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);
}

}  // namespace leveldb
arena.cc
namespace leveldb {

<!默认块大小4096 Byte>
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];
  }
}

<!1、申请的内存大于1024时,直接从内存申请返回,避免对现有剩下的内存的浪费。
  2、申请内存小于等于1024时,直接向系统申请4096内存大小
     并重新赋值alloc_ptr、alloc_bytes_remaining_,这样的话
     如果之前的block中还剩余内存,则直接浪费掉了。
  >
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::AllocateAligned(size_t bytes) {
  const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; //最少以8字节对齐
  <!对齐必须是2的倍数>
  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);
  <!如果current_mod不是0,就求出与对齐大小的差值>
  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 {
    // AllocateFallback always returned aligned memory
    result = AllocateFallback(bytes);
  }
  <!最后检测下返回的地址是否对齐的。>
  assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);
  return result;
}

<!new一个内存,并计数下来。>
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;
}

}  // namespace leveldb
三、总结

总的来说Arena只是针对简单实用的,不会涉及到内存的回收,内存申请时的合适内存块查询、内存的定期合并等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值