前言

对于一个高性能的服务器程序来说,内存的使用非常重要。C++提供new/delete来管理内存的申请和释放,但是对于小对象来说,直接使用new/delete代价比较大,要付出额外的空间和时间,性价比不高。另外,也需要避免多次申请和释放引起的内存碎片。一旦碎片到达一定程度,即使剩余内存足够用,但由于缺乏足够的连续的空闲空间,导致内存不够用的假象。

C++ STL为避免内存碎片实现了一个复杂的内存池,leveldb则没有那么复杂,只是实现了一个“一次性”内存池Arena。leveldb并不是所有地方都使用了该内存池,主要是memtable使用,主要是用于临时存放用户的更新数据,由于更小的数据可能较小,所以这里使用内存池就很合适。

原理

为避免小对象的频繁分配,需要减少对new的调用,最简单的做法就是申请大块的内存,多次分给客户。leveldb使用一个vector<char*>来保存所有内存分配记录表,默认每次申请4k的内存,记录下剩余指针和剩余内存字节数。每当有新的申请,如果当前剩余的字节能满足需求,则直接返回给用户,否则,对于超过1k的申请,直接new返回,小于1k的申请,则申请一个新的4k块,从中分配一部分给用户。

但这样存在一个问题当前块剩余部分就浪费了,改进的方法,针对每个block都记录剩余字节数,但如此需要遍历查找合适的block,要付出一定性能的代价。谷歌的做法是浪费就浪费吧。至于释放就需要释放整个内存池来释放所占的内存,这个和leveldb的需求有关,memtable不需要释放单次内存,flush到硬盘后整个memtable销毁。

源码实现

arena.h

#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_
#define STORAGE_LEVELDB_UTIL_ARENA_H_ #include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <vector> 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 {
return memory_usage_.load(std::memory_order_relaxed);
} private:
char* AllocateFallback(size_t bytes);
char* AllocateNewBlock(size_t block_bytes); char* alloc_ptr_;
size_t alloc_bytes_remaining_;
std::vector<char*> block_;
std::atomic<size_t> memory_usage_;
}; inline char* Arena::Allocate(size_t bytes) {
assert(bytes > );
if (bytes <= alloc_bytes_remaining_) {
char* result = alloc_ptr_;
alloc_ptr_ += bytes;
alloc_bytes_remaining_ -= bytes;
return result;
}
return AllocateFallback(bytes);
} } #endif

arena.cc

#include "arena.h"

namespace leveldb {

static const int kBlockSize = ;

Arena::Arena()
: alloc_ptr_(nullptr), alloc_bytes_remaining_(), memory_usage_(){} Arena::~Arena() {
for (size_t i = ; i < block_.size(); i++) {
delete[] block_[i];
}
} char* Arena::AllocateFallback(size_t bytes) {
if (bytes > kBlockSize / ) {
char* result = AllocateNewBlock(bytes);
return result;
} 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*) > ) ? sizeof(void*) : ;
static_assert((align & (align - )) == ,
"Pointer size should be a power of 2");
size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - );
size_t slop = (current_mod == ? : 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 {
result = AllocateFallback(bytes);
}
assert((reinterpret_cast<uintptr_t>(result) & (align - )) == );
return result;
} char* Arena::AllocateNewBlock(size_t block_bytes) {
char* result = new char[block_bytes];
block_.push_back(result);
memory_usage_.fetch_add(block_bytes + sizeof(char*),
std::memory_order_relaxed);
return result;
} }

参考博客:https://www.cnblogs.com/shenzhaohai1989/p/3904808.html

leveldb Arena源码分析的更多相关文章

  1. Leveldb源码分析--1

    coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...

  2. tair源码分析——leveldb新增的CompactRangeSelfLevel过程

    tair是一个分布式KV存储引擎,当新增机器或者有机器down掉的时候,tair的dataserver会根据ConfigServer生成的新的对照表进行数据的迁移和清理.在数据清理的过程中就用到了在t ...

  3. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  4. leveldb源码分析--WriteBatch

    从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...

  5. leveldb源码分析--Key结构

    [注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...

  6. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  7. 分布式存储Seaweedfs源码分析

    基于源码版本号 0.67 , [Seaweedfs以前旧版叫Weedfs]. Seaweedfs 是一个非常优秀的由 golang 开发的分布式存储开源项目, 虽然在我刚开始关注的时候它在 githu ...

  8. 并发编程之 Exchanger 源码分析

    前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...

  9. Netty源码分析第5章(ByteBuf)---->第4节: PooledByteBufAllocator简述

    Netty源码分析第五章: ByteBuf 第四节: PooledByteBufAllocator简述 上一小节简单介绍了ByteBufAllocator以及其子类UnPooledByteBufAll ...

随机推荐

  1. laravel中遇到的坑

    已经遇到的坑和未来可能遇到的坑都将在这里写出来: 在资源控制器中创建新的方法后(如果资源控制器中的7个方法无法满足你的需求时,你就会创建新的方法),接下来就是创建路由,这个时候注意了,你必须要把路由放 ...

  2. pdf缩略图上传组件

    之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...

  3. URAL 2036 Intersect Until You're Sick of It 形成点的个数 next_permutation()函数

    A - Intersect Until You're Sick of It Time Limit:500MS     Memory Limit:65536KB     64bit IO Format: ...

  4. 微信小程序_(校园视)开发上传视频业务

    微信小程序_(校园视) 开发用户注册登陆 传送门 微信小程序_(校园视) 开发上传视频业务 传送门 微信小程序_(校园视) 开发视频的展示页-上 传送门 微信小程序_(校园视) 开发视频的展示页-下 ...

  5. [CSP-S模拟测试]:循环依赖(拓扑)

    题目传送门(内部题148) 输入格式 每个测试点第一行为一个正整数$T$,表示该测试点内的数据组数. 接下来$T$组数据,每组数据第一行一个正整数$n$,表示有引用单元格进行计算的单元格数,接下来$n ...

  6. iPhone/iPad调整事件递交

    UIKit 为应用程序提供了编程手段来简化事件处理或者完全关闭事件流.下面的列表总结了这些方法: 关闭触摸事件的递交. 缺省情况下,视图接收触摸事件,但是你可以设置它的userInteractionE ...

  7. Java写入的常用技巧

    一.批量写入 Java写入大量数据到磁盘/数据库等其它第三方介质时,由于IO是比较耗费资源的操作,通常采用攒一批然后批量写入的模式 //通常构造一个缓存池,一个限制指标,可以是内存大小也可以是时间 B ...

  8. Word文档怎么从第二页加页码

    1.首先将光标放到首页的最后位置 2.“页面布局”—“分隔符”—“下一页” 3.“插入”—“页码”—选一种样式的页码 4.将光标定位到第二页的页脚处,“设计”—取消“链接到前一条页眉” 5.将第二页的 ...

  9. CentOS7 开机启动脚本与命令后台运行

    一.& 在 Linux 命令后加上 &  可以在后台运行 二.nohup 对 SIGHUP 信号免疫,对 SIGINT 信号不免疫,可用 shopt | grep hup 查看. 当关 ...

  10. Struts2.3+Spring3.2+Hibernate4.2框架搭建

    一.环境 SSH使用的版本:struts2.3.14.spring3.2.2.hibernate4.2.0 数据库:MYSQL tomcat版本:apache-tomcat-7.0.42 二.所需要导 ...