zoukankan      html  css  js  c++  java
  • 【LevelDB源码阅读】Arena

    是什么

    内存分配管理器,主要为skiplist即Memtable服务而不是整个项目。申请内存时,将申请到的内存直接放入vector中,在Arena的生命周期结束后,统一释放掉所有申请的内存,内部结构如下图:

    为什么要用

    • 避免内存碎片,skiplist里面记录的都是用户传进来的key/value,这些字符串有长有短,放到内存中很容易导致内存碎片

    学到什么

    • 与(&)操作进行内存对齐
    • atomic保证原子性
    • 使用delete避免拷贝和赋值操作

    源码分析

    首先看一下内部成员

      // 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_;
    

    构造函数和析构函数

    Arena::Arena()  // vector调用默认构造函数初始化
        : alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {}
    
    Arena::~Arena() {
      for (size_t i = 0; i < blocks_.size(); i++) {
        delete[] blocks_[i];
      }
    }
    

    拷贝构造和拷贝赋值

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

    主要对外接口

      // 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);
    

    具体实现如下:

    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); // 需要内存大于剩余内存
    }
    

    需要内存大于剩余内存时,调用AllocateFallback()分配内存:

    • 如果需要内存大于4096/4,则直接分配一块大小为bytes内存,避免每次剩余内存不可用,造成浪费。
    • 否则,重新分配一个内存块(默认大小4096)用于存储数据,虽然浪费了当前内存块的剩余内存,但当下次再分配小内存时,可以直接使用,减少内存分配次数。

    具体实现如下:

    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;
    }
    

    其中分配新内存块实现如下:
    memory_order_relaxed:针对只要求原子操作,除此之外不需要其它同步保证,计数器是一种典型应用场景。

    char* Arena::AllocateNewBlock(size_t block_bytes) {
      char *result = new char[block_bytes];
      blocks_.push_back(result);  // 申请块放入vector,以便析构函数释放内存
      memory_usage_.fetch_add(block_bytes + sizeof(char*),
                              std::memory_order_relaxed);
      return result;
    }
    

    对齐内存分配

    char* Arena::AllocateAligned(size_t bytes) {
      const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;  // 与系统相关,4或8
      static_assert((align & (align - 1)) == 0,
                    "Pointer size should be a power of 2");  // 确保是2的指数次方
      size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - 1);  // 当前指针模对齐值
      size_t slop = (current_mod == 0 ? 0 : align - current_mod);  // 还差slop个字节对齐
      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;
    }
    

    读取内存使用

      // 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);
      }
    
  • 相关阅读:
    最大流最小割
    最大权闭合图
    凸包,多边形面积,线段在多边形内的判定。
    模线性方程
    ZOJ Monthly, August 2014
    nenu contest2
    2014 Multi-University Training Contest 10
    Codeforces Round #262 (Div. 2)
    nenu contest
    poj 2299 求逆序数
  • 原文地址:https://www.cnblogs.com/galaxy-hao/p/13070332.html
Copyright © 2011-2022 走看看