内存管理、内存池

内存管理

内存申请调用

在这里插入图片描述

小插曲:new为什么不需要指明申请的大小
  • 其new底层编译时会对其展开,其operator new(size_t ),其在编译时就确定了new 申请对应的类型,其大小就被默认写死到对应函数,所以相应重载该operator new(size_t ),第一个参数类型是不能修改的

malloc内存申请

malloc申请空间时,记录其空间大小(其空间上方,有一个数据头,头部信息就记录了申请空间的大小),当调用free函数时,即需要读取头部信息得到需要释放的字节数

在这里插入图片描述

申请单个元素

在这里插入图片描述

申请连续空间
  • 比申请单个元素空间多了计数,该计数用于delete[] 时读取需要析构几次指向其他地方的内存,而若不使用delete [],则导致只析构一处内存,其他元素指向将发生内存泄露
    在这里插入图片描述

  • array new可能发生的内存泄露

  • 若array new没有相应的array delete,造成内存泄露,其不是直接申请块内存泄露,而是间接指向造成内存泄露

glibc堆分配算法

其采取多种算法组合

  1. 它对于小于64bit的空间申请总是类似于对象池的方法 (较少的内部碎片)
  2. 对于大于64bit而小于512bit的,它会根据情况采取上述方法的最佳折中策略
  3. 对于大于512字节的空间申请采用的是最佳适配算法
  4. 对于大于128KB的申请,它会使用mmap机制直接向操作系统申请空间。

对象池

一些场合,被分配对象的大小是较为固定的几个值,这时候我们可以针对这样的特征设计一个更为高效的堆算法,称为对象池
对象池思路很简单,如果每一次分配的空集大小都一样,那么就可以按照这个每次请求分配的大小作为一个单位,把整个堆空间划分为大量的小块,每次请求的时候只需要找到小块就可以了。
对象池每次仅需请求一个固定的大小,因此实现起来很容易。由于每次总是只请求一个单位的内存,因此请求得到满足的速度非常快,无需查找一个足够大的空间。
对每一个需要处理的类A,B,C都重写operator new,那我们是否可以将这同一个功能集中到一个地方去吗?全局的函数?一个类?

对象池实例1
#include <cstddef>
#include <iostream>
namespace sqh
{
//ref. C++Primer 3/e, p.765
//per-class allocator 

class Screen {
public:
    Screen(int x) : i(x) { };
    int get() { return i; }

    void* operator new(size_t);
    void  operator delete(void*, size_t);	//(2)
//! void  operator delete(void*);			//(1) 二擇一. 若(1)(2)並存,會有很奇怪的報錯 (摸不著頭緒) 
	    
private:
    Screen* next;
    static Screen* freeStore; //指向空闲块的头
    static const int screenChunk;//桶大小
private:
    int i;
};
Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;

void* Screen::operator new(size_t size)
{
  Screen *p;
  if (!freeStore) {
      //linked list 是空的,所以攫取一大塊 memory
      //以下呼叫的是 global operator new
      size_t chunk = screenChunk * size;
      freeStore = p =
         reinterpret_cast<Screen*>(new char[chunk]);//攫取一大塊 memory,new char[chunk]其没有构造语义,我们此时只想申请内存
      //將分配得來的一大塊 memory 當做 linked list 般小塊小塊串接起來
      for (; p != &freeStore[screenChunk-1]; ++p)
          p->next = p+1;
      p->next = 0;
  }
  p = freeStore;
  freeStore = freeStore->next;
  return p;
}


//! void Screen::operator delete(void *p)		//(1)
void Screen::operator delete(void *p, size_t)	//(2)二擇一 
{
  //將 deleted object 收回插入 free list 前端
  (static_cast<Screen*>(p))->next = freeStore;//头插
  freeStore = static_cast<Screen*>(p);
}

//-------------
void test_per_class_allocator_1()
{	
	cout << "\ntest_per_class_allocator_1().......... \n";	
		
   	cout << sizeof(Screen) << endl;		//8	

size_t const N = 100;
Screen* p[N];	

   	for (int i=0; i< N; ++i)
   	     p[i] = new Screen(i);         

	//輸出前 10 個 pointers, 用以比較其間隔 
   	for (int i=0; i< 10; ++i)  	   
		cout << p[i] << endl;     
    
   	for (int i=0; i< N; ++i)
   	     delete p[i];     	
}
} //namespace
对象池实例2
#include <cstddef>
#include <iostream>
namespace sqh
{
//ref. Effective C++ 2e, item10
//per-class allocator 

class Airplane {   //支援 customized memory management
private:
  	struct AirplaneRep {
    	unsigned long miles;
    	char type;
  	};
private:
	//内嵌结构有数据了吗,每个节点是一个联合体
  	union {
    	AirplaneRep rep;  //此針對 used object
    	Airplane* next;   //此針對 free list----》Embedded pointer 内嵌指针
  	};
public:
  	unsigned long getMiles() { return rep.miles; }
  	char getType() { return rep.type; }
  	void set(unsigned long m, char t)
  	{
     	rep.miles = m;
     	rep.type = t;
  	}
public:
  	static void* operator new(size_t size);
  	static void  operator delete(void* deadObject, size_t size);
private:
  	static const int BLOCK_SIZE;
  	static Airplane* headOfFreeList;//被所有该类对象所共享,当申请10W个对象,那么当释放时,将会构建10W个对象大小的链表
};

Airplane* Airplane::headOfFreeList;  
const int Airplane::BLOCK_SIZE = 512;   

void* Airplane::operator new(size_t size)
{
  	//如果大小錯誤,轉交給 ::operator new()
  	if (size != sizeof(Airplane))
    	return ::operator new(size);

  	Airplane* p = headOfFreeList;  

  	//如果 p 有效,就把list頭部移往下一個元素
  	if (p)
    	headOfFreeList = p->next;
  	else {
    	//free list 已空。配置一塊夠大記憶體,
    	//令足夠容納 BLOCK_SIZE 個 Airplanes
    	Airplane* newBlock = static_cast<Airplane*>
       		(::operator new(BLOCK_SIZE * sizeof(Airplane)));
    	//組成一個新的 free list:將小區塊串在一起,但跳過 
    	//#0 元素,因為要將它傳回給呼叫者。
    	for (int i = 1; i < BLOCK_SIZE-1; ++i)
      		newBlock[i].next = &newBlock[i+1];
    	newBlock[BLOCK_SIZE-1].next = 0; //以null結束

    	// 將 p 設至頭部,將 headOfFreeList 設至
    	// 下一個可被運用的小區塊。
    	p = newBlock;
    	headOfFreeList = &newBlock[1];
  	}
  	return p;
}

// operator delete 接獲一塊記憶體。
// 如果它的大小正確,就把它加到 free list 的前端
void Airplane::operator delete(void* deadObject,
                               size_t size)
{
  	if (deadObject == 0) return;          
  	if (size != sizeof(Airplane)) {   
    	::operator delete(deadObject);
    	return;
  	}

  	Airplane *carcass =
    	static_cast<Airplane*>(deadObject);

  	carcass->next = headOfFreeList;
  	headOfFreeList = carcass;
}

//-------------
void test_per_class_allocator_2() 
{	
	cout << "\ntest_per_class_allocator_2().......... \n";		
	
  	cout << sizeof(Airplane) << endl;    //8

size_t const N = 100;
Airplane* p[N];	

   	for (int i=0; i< N; ++i)
   	     p[i] = new Airplane;     
			
    
    //隨機測試 object 正常否 
  	p[1]->set(1000,'A'); 	
  	p[5]->set(2000,'B');
  	p[9]->set(500000,'C');
  	cout << p[1] << ' ' << p[1]->getType() << ' ' << p[1]->getMiles() << endl;
  	cout << p[5] << ' ' << p[5]->getType() << ' ' << p[5]->getMiles() << endl;
  	cout << p[9] << ' ' << p[9]->getType() << ' ' << p[9]->getMiles() << endl; 
  	
	//輸出前 10 個 pointers, 用以比較其間隔 
   	for (int i=0; i< 10; ++i)  	   
		cout << p[i] << endl; 		 
	 
   	for (int i=0; i< N; ++i)
   	     delete p[i]; 	
}
} //namespace
对象池实例3 allocator
namespace sqh03
{
	
class allocator 
{
private:
  	struct obj { //为什么这是内嵌指针?allocator是对象的一部分,allocator成员为obj*next  nb的手法
    	struct obj* next;  //embedded pointer
  	};	
public:
    void* allocate(size_t);
    void  deallocate(void*, size_t);
    void  check();
    
private: 
    obj* freeStore = nullptr;
    const int CHUNK = 5; //小一點方便觀察分配的5的区块空间连续 
};

void* allocator::allocate(size_t size)
{
  	obj* p;

  	if (!freeStore) {
      	//linked list 是空的,所以攫取一大塊 memory
      	size_t chunk = CHUNK * size;
      	freeStore = p = (obj*)malloc(chunk);  
      
      	//cout << "empty. malloc: " << chunk << "  " << p << endl;
     
      	//將分配得來的一大塊當做 linked list 般小塊小塊串接起來
      	for (int i=0; i < (CHUNK-1); ++i)	{  //沒寫很漂亮, 不是重點無所謂.  
           	p->next = (obj*)((char*)p + size);
           	p = p->next;
      	}
      	p->next = nullptr;  //last       
  	}
  	p = freeStore;
  	freeStore = freeStore->next;
   
  	//cout << "p= " << p << "  freeStore= " << freeStore << endl;
  
  	return p;
}
void allocator::deallocate(void* p, size_t)
{
  	//將 deleted object 收回插入 free list 前端
  	((obj*)p)->next = freeStore;
  	freeStore = (obj*)p;
}
void allocator::check()
{ 
    obj* p = freeStore;
    int count = 0;
    
    while (p) {
        cout << p << endl;
		p = p->next;
		count++;
	}
    cout << count << endl;
}
//--------------

class Foo {
public: 
	long L;
	string str;
	static allocator myAlloc;
public:
	Foo(long l) : L(l) {  }
	static void* operator new(size_t size)
  	{     return myAlloc.allocate(size);  	}
  	static void  operator delete(void* pdead, size_t size)
    {     return myAlloc.deallocate(pdead, size);  }
};
allocator Foo::myAlloc;


class Goo {
public: 
	complex<double> c;
	string str;
	static allocator myAlloc;
public:
	Goo(const complex<double>& x) : c(x) {  }
	static void* operator new(size_t size)
  	{     return myAlloc.allocate(size);  	}
  	static void  operator delete(void* pdead, size_t size)
    {     return myAlloc.deallocate(pdead, size);  }
};
allocator Goo::myAlloc;

//-------------	
void test_static_allocator_3()
{
	cout << "\n\n\ntest_static_allocator().......... \n";	

{	
Foo* p[100];
 
	cout << "sizeof(Foo)= " << sizeof(Foo) << endl;
   	for (int i=0; i<23; ++i) {	//23,任意數, 隨意看看結果 
	   	p[i] = new Foo(i); 
	    cout << p[i] << ' ' << p[i]->L << endl;
	}
	//Foo::myAlloc.check();
	
   	for (int i=0; i<23; ++i) {
	   	delete p[i]; 
	}	
	//Foo::myAlloc.check();
}

{	
Goo* p[100];
 
	cout << "sizeof(Goo)= " << sizeof(Goo) << endl;
   	for (int i=0; i<17; ++i) {	//17,任意數, 隨意看看結果 
	   	p[i] = new Goo(complex<double>(i,i)); 
	    cout << p[i] << ' ' << p[i]->c << endl;
	}
	//Goo::myAlloc.check();
	
   	for (int i=0; i<17; ++i) {
	   	delete p[i]; 
	}	
	//Goo::myAlloc.check();	
}
}
} //namespace	
macro优化对象池
namespace jj10
{	
using sqh03::allocator;

//借鏡 MFC macros. 
// DECLARE_POOL_ALLOC -- used in class definition
#define DECLARE_POOL_ALLOC() \
public: \
    void* operator new(size_t size) { return myAlloc.allocate(size); } \
    void operator delete(void* p) { myAlloc.deallocate(p, 0); } \
protected: \
    static allocator myAlloc; 

// IMPLEMENT_POOL_ALLOC -- used in class implementation file
#define IMPLEMENT_POOL_ALLOC(class_name) \
allocator class_name::myAlloc; 


// in class definition file
class Foo {
   DECLARE_POOL_ALLOC()
public: 
	long L;
	string str;
public:
	Foo(long l) : L(l) {  }   
};
//in class implementation file
IMPLEMENT_POOL_ALLOC(Foo) 


//  in class definition file
class Goo {
   DECLARE_POOL_ALLOC()
public: 
	complex<double> c;
	string str;
public:
	Goo(const complex<double>& x) : c(x) {  }   
};
//in class implementation file
IMPLEMENT_POOL_ALLOC(Goo) 


void test_macros_for_static_allocator()
{
	cout << "\n\n\ntest_macro_for_static_allocator().......... \n";
		
Foo* pF[100];
Goo* pG[100];
 
	cout << "sizeof(Foo)= " << sizeof(Foo) << endl;
	cout << "sizeof(Goo)= " << sizeof(Goo) << endl;	
	
   	for (int i=0; i<23; ++i) {	//23,任意數, 隨意看看結果 
	   	pF[i] = new Foo(i); 
	   	pG[i] = new Goo(complex<double>(i,i)); 	   	
	    cout << pF[i] << ' ' << pF[i]->L << "\t";
	    cout << pG[i] << ' ' << pG[i]->c << "\n";	    
	}
	
   	for (int i=0; i<23; ++i) {
	   	delete pF[i]; 
	   	delete pG[i]; 	   	
	}	
	
}
} //namespace
内嵌指针

在这里插入图片描述

poll_alloc 对象池

在这里插入图片描述

  • 其不带相应的头部信息,当其相应桶管理的内存不足时,才去调用malloc申请内存,以此来减少malloc申请导致的overhead

对于GUN4.9版,容器实现内存池分配器
在这里插入图片描述

bitmap_allocator

  • 对于多次申请较小内存的类型元素,其overhead很大,其内存利用率很低,是对内存的极度浪费
    在这里插入图片描述
placement new 定位new
  • 可对一块空间进行重新调用构造函数
  • 可用于对array new的重新赋值,同等于调用构造函数
Object *s=new Object(10);
new(s) Object(100);//定位new,以s所指向的空间(不开辟空间)调动构造函数构造对象
...
delete s

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值