简易内存分配器的实现

一个简易的C++内存管理器实现总体结构如下所示:

该内存分配器的刚开始使用是通过sc_pool_s *sc_create_pool (size_t size)函数创建一个字节大小为size的sc_pool_s对象,该对象由一个链表结构的sc_pool_data_t和一个指向sc_pool_large_s的结构体指针组成,其中sc_pool_data_t用来处理小内存申请的需求,而sc_pool_large_s则用来处理大内存申请的请求,且根据需要动态的扩展,形成一个链表结构。

一些其他内容的说明

1. 内存对齐方式的实现

#define sc_align_ptr(p, a)                                                     (unsigned char *) (((unsigned long int ) (p) + ((unsigned long int ) a - 1)) & ~((unsigned long int ) a - 1))

这段代码的意思就是返回一个以大小为a的内存对齐方式,要解释这段代码,假设里面的变量都是unsigned long类型的,将其简化成如下形式:

(p +(a - 1)) & ~(a - 1)

? 先解释~(a - 1),假设现在的对齐方式是16字节的方式对齐(一般都是将地址2的幂次进行对齐),即a = 16,现在将a - 1取反后的二进制为10000,将这个二进制数与任何数相与,都会产生后4位为0的二进制数,而其他位数不变,这个数此时恰好就是a = 16的倍数。而p + (a - 1)则是要获取比起始地址p要大的数,且这个数要尽可能的不会影响原来已经分配了内存的空间,但又不会比p大太多从而造成内存碎片,则所加的数是a-1满足该要求。

? 例如此时p的基址为17(假设从0开始分配,已经为某处分配了17字节的大小的内存,现在从p=17开始申请内存)。p + (a - 1)的值为0...010001 + 0...01111 = 100000,此时(p +(a - 1)) & ~(a - 1)相与的结果是0...0100000 & 1...10000 =0...0100000 = 32;即地址不会从17开始,而只会从32开始,有利于快速读取操作。

内存对齐方式的原因

  1. 内存对齐后,可以更方便读取的存取数据。
  2. 不是全部硬件平台都能访问随意地址上的随意地址,为了兼容性,需要做内存对齐。

解释

? 对于大多数语言来说,这部分实现是由编译器来实现的(编译器将每个”数据单元“放在合适的内存位置上),但是C/C++太强大,太灵活了,允许你去干预”内存对齐“。

以如下程序做说明:

struct{
    char a;
    char b;
    int c;
} Struct1;
struct{
    char a;
    int c;
    char b;
} Struct2;
int main()
{
    cout << "Struct1 : " << sizeof(Struct1) << endl;  // Struct1 : 8
    cout << "Struct2 : " << sizeof(Struct2) << endl;  // Struct2 : 12
    return 0;
}

原因是linux中默认对齐方式的值为4,在struct1中,前两个字节表示共占2字节的大小,第一块内存的位置可以放下,然后一个int占4个字节,而第一个内存里只能容下两个字节的数据存放,所以就会放到第二块内存位置中去;同理,在结构体struct2中,第一块内存只能容纳一个char类型的字符,而4字节的int要放到 第二块内存里,第三个char此时只能放到 第三块内存中去了。

结构体 内存块1(4字节) 内存块2(4字节) 内存块3(4字节)
Struct1 a,b c
Struct2 a c b

经过内存对齐后,CPU的内存访问速度大大提升。如果操作1字节的数据,可以是任意地址;若是操作2字节的数据,如果开始地址在偶数地址,一次就可以取2字节,如果开始地址在奇数,就要2次内存操作才能完成;如果操作4字节的数据,最好开始地址在能被4整除的数值上,这样可以用一条32位的内存操作指令完成。同样,8字节的开始位置最好的能被8整除的数值上,这样可以用一条64位的内存操作指令完成。就是说,如果对齐了,一次就可以完成,不对齐,就可能多次才能完成。这样,只要你在结构体里对象之间能处理好对齐,你的数据就能操作得很快。

2. 函数 posix_memalign() 使用说明

功能:返回size字节的动态内存,预对齐内存的分配。posix_memalign函数的用法类似于malloc的用法,由posix_memalign分配的内存空间,需要由free释放。

头文件:#include <stdlib.h>

函数原型:int posix_memalign (void **memptr,size_t alignment,size_t size);

参数:

  • memptr 分配好的内存空间的首地址
  • alignment 对齐边界,Linux中,32位系统是8字节,64位系统是16字节
  • size 指定分配size字节大小的内存

返回值:调用posix_memalign( )成功时会返回size字节的动态内存,并且这块内存的地址是alignment的倍数。参数alignment必须是2的幂,还是void指针的大小的倍数。返回的内存块的地址放在了memptr里面,函数返回值是0。
调用失败时,没有内存会被分配,memptr的值没有被定义,返回如下错误码之一:
EINVAL:参数不是2的幂,或者不是void指针的倍数。

ENOMEM:没有足够的内存去满足函数的请求。

使用案例

int main(){

   sc_pool_t *pool = sc_create_pool(1024);

    int i=0;

    for(i=0;i<10000;i++){

        sc_pcalloc (pool,800 * 10);

    }

    sleep(5);

    sc_destroy_pool (pool);

    sleep(5);
}

下载地址 github

参考资料

函数 posix_memalign() 使用说明

原文地址:https://www.cnblogs.com/helloworldcode/p/10656756.html

时间: 2024-10-24 18:31:25

简易内存分配器的实现的相关文章

SGI-STL简记(一)-内存分配器解析

defalloc.h : 默认分配器allocator: 早期专用于HP的默认分配器,目前是不被赞成使用的: 仅提供了allocator模板简单封装,重声明(或类型外抛)一些必要的类型Type,此外提供申请和释放内存接口(底层直接使用全局::operator new和::operator delete进行内存分配和释放): 取引用的地址.取常引用的常量地址等接口: allocate:内部取消new申请内存时失败跳转处理函数而是直接抛出异常: init_page_size:申请页大小下最大空间大小

说下Redis采用不同内存分配器

参考文章: http://blog.sina.com.cn/s/blog_51df3eae01016peu.html 我们知道Redis并没有自己实现内存池,没有在标准的系统内存分配器上再加上自己的东西.所以系统内存分配器的性能及碎片率会对Redis造成一些性能上的影响. 在Redis的 zmalloc.c 源码中,我们可以看到如下代码: 49 #if defined(USE_TCMALLOC) 50 #define malloc(size) tc_malloc(size) 51 #define

简单的内存分配器

采用自定义的operator运算符实现自己的内存分配策略,在某些时候可以提高程序的效率.   C++中的new运算符,具体工作流程如下: 1.调用operator new申请原始内存 2.调用place new表达式,执行类的构造函数 3.返回内存地址 而delete操作符的工作是: 1.调用对象的析构函数 2.调用operator delete释放内存 例如: #include <iostream> using namespace std; class Test { public: Test

[转]STL的内存分配器

题记:内存管理一直是C/C++程序的红灯区.关于内存管理的话题,大致有两类侧重点,一类是内存的正确使用,例如C++中new和delete应该成对出现,用RAII技巧管理内存资源,auto_ptr等方面,很多C/C++书籍中都使用技巧的介绍.另一类是内存管理的实现,如linux内核的slab分配器,STL中的allocator实现,以及一些特定于某种对象的内存管理等.最近阅读了一些内存管理实现方面的资料和源码,整理了一下,汇编成一个系列介绍一些常用的内存管理策略. 1. STL容器简介 STL提供

内存分配器 (Memory Allocator)

对于大多数开发者而言,系统的内存分配就是一个黑盒子,就是几个API的调用.有你就给我,没有我就想别的办法.来UC前,我就是这样认为的.实际深入进去时,才发现这个领域里也是百家争鸣,非常热闹.有操作系统层面的内存分配器(Memory Allocator),有应用程序层面的,有为实时系统设计的,有为服务程序设计的.但他们的目的确认一样的,平衡内存分配的性能和提高内存使用的效率. 从浏览器开发的角度看,手机内存的增长速度相对于网页内容的增长仍然只是温暖水平,像Android这样的用内存大户更要算计着用

STL中的内存分配器原理

题记:内存管理一直是C/C++程序的红灯区.关于内存管理的话题,大致有两类侧重点,一类是内存的正确使用,例如C++中new和delete应该成对出现,用RAII技巧管理内存资源,auto_ptr等方面,很多C/C++书籍中都使用技巧的介绍.另一类是内存管理的实现,如linux内核的slab分配器,STL中的allocator实现,以及一些特定于某种对象的内存管理等.最近阅读了一些内存管理实现方面的资料和源码,整理了一下,汇编成一个系列介绍一些常用的内存管理策略. 1. STL容器简介 STL提供

内核的bootmem内存分配器【转】

转自:http://blog.csdn.net/zmxiangde_88/article/details/8041040 版权声明:本文为博主原创文章,未经博主允许不得转载. 在内核启动期间,伙伴系统内存管理器还没有建立之前,内核此时也要分配内存以及创建一些用于建立内存管理等机制的数据结构,此时内存分配和管理就是由bootmem内存分配器来完成的. bootmem的建立要求就是简单,越简单越好,因为一旦伙伴系统建立之后,就不需要bootmem了,因此对性能和通用性等要服从一切从简的原则.在了解这

遵照std::allocator实现自定制的内存分配器

下面是标准库STL中allocator的实现 template<class _Ty> class allocator : public _Allocator_base<_Ty> { // generic allocator for objects of class _Ty public: typedef allocator<_Ty> other; typedef _Allocator_base<_Ty> _Mybase; typedef typename _

[转]Linux内核最新的连续内存分配器(CMA)——避免预留大块内存

http://blog.csdn.net/21cnbao/article/details/7309757 在我们使用ARM等嵌入式Linux系统的时候,一个头疼的问题是GPU,Camera,HDMI等都需要预留大量连续内存,这部分内存平时不用, 但是一般的做法又必须先预留着.目前,Marek Szyprowski和Michal Nazarewicz实现了一套全新的Contiguous Memory Allocator.通过这套机制,我们可以做到不预留内存,这些内存平时是可用的,只有当需要的时候才