C ++ memory management technology

C++ membery primitives

distribution freed category Could Overload
malloc free C function Can not
new delete C ++ expressions Can not
::operator new ::operator delete C ++ function can
allocator< T >::allocate allocator< T >::deallocate C ++ Standard Library It can be freely designed to match any container
  • The fourth method has different ways on different compiler versions
#ifdef _MSC_VER
    int *p4 = allocator<int>().allocate(3, (int*)0);
    allocator<int>().deallocate(p4, 3);
#endif

#ifdef __BORLANDC__
    int *p4 = allocator<int>().allocate(5);
    allocator<int>().deallocate(p4, 5);
#endif

#ifdef __GNUC__
    // 在早期的 GUNC 中使用这种调用方法
    void *p4 = alloc::allocate(512);
    alloc::deallocate(p4, 512);
    // 在后期的 GUNC 中进行了进一步的优化和区分
    int *p4 = allocator<int>().allocate(7);
    allocator<int>().deallocate((int*)p4, 7);
    // 而早期的 alloc 仍然十分重要,于是换成了这种写法
    void *p5 = __gnuc_cxx::__pool_alloc<int>().allocate(9);
    __gnuc_cxx::__pool_alloc<int>().deallocate((int*)p5, 9);
#endif

new/delete expression

new will allocate memory, and then call the constructor, so concrete realization of new follows, but usually overloaded new, are overloaded :: operator new

Complex *pc = new Complex(1, 2);
            ↓↓
try {
    void *mem = operator new(sizeof(Complex));// operator new 的实现调用了 malloc
    pc = static_cast<Complex*>(mem);
    pc->Complex::Complex(1, 2);
    // 在 GCC 中用户无法直接使用构造函数
} catch(std::bad_alloc) {
}

delete pc;
    ↓↓
pc->Complex::~Complex();
operator delete(pc);    // operaotr delete 的实现调用了 free

array new/delete

By opening up a new array will assign a 4-byte array in the starting position cookie, a cookie which records the length of the dispensing array. If you use the delete release will only release one of them, the other block memory leak occurs while using delete [] release, you can apply to ensure that the memory is completely released.

Process for constructing arrays and deleted, according to the standard sequence calling a constructor 0,1,2, 2,1,0 press table sequentially calling the destructor.

placement new

placement new allows us to construct an object in an already allocated memory

char *buf = new char[sizeof(Complex)*3];
Complex *pc= new(buf)Complex(1, 2);
            ↓↓
try {
    void *mem = operator new(sizeof(Complex), buf);
    // 和 new 不同的地方在于多传了 buf 参数,于是 operator new 什么也不做直接返回 buf
    pc = static_cast<Complex*>(mem);
    pc->Complex::Complex(1, 2);
} catch(std::bad_alloc) {
}

class allocate

malloc there were four bytes of memory allocation cookie in end to end, the recording size of the memory allocated. Therefore, frequent malloc memory will cause a large part of the waste.

Technical solution of this approach is through the memory pool technique, a large disposable I can assign memory to allocate memory in the subsequent process, a portion of the dispensed for use from the bulk of the program.

  • The first edition of the memory management techniques are as follows
class Screen{
public:
	static void* operator new(size_t size) {
		Screen *p;
		if(!freeStore){	// 如果没有可以用的内存空间,则再分配一次
			size_t chunk = screenChunk * size;
			freeStore = p = reinterpret_cast<Screen*> (new char[chunk]);	// char 字节为 1
			for(; p != &freeStore[screenChunk - 1]; ++ p){
				p -> next = p + 1; // 将得到的内存串成链表
			}
			p -> next = NULL;
		}
		p = freeStore;
		freeStore = freeStore -> next;
		return p;
	}
	static void operator delete(void *p) {
		(static_cast<Screen*>(p))->next = freeStore;
        // 显然,把 delete 掉的内存空间直接插入到头部,是最快的插入方式
		freeStore = static_cast<Screen*>(p);
	}
private:
    Screen *next;
    static Screen *freeStore;	// 将可用内存用链表形式存储,存下链表头指针
    static const int screenChunk;	// 选择一次性分配的内存空间
private:
    int i;
    /* 在第二版中,使用如下方法
    union {
        int i;
        Screen *next;
    }
        因为 next 只有在没有使用的时候才有意义,i 只有在被使用的时候才有意义,所以可以使用 union 来节省内存的消耗
    */
};
Screen* Screen::freeStore = NULL;
const int Screen::screenChunk = 24;
int main() {
	// freopen("in", "r", stdin);
	Screen *p[100];
	for(int i=0; i<100; i++) {
		p[i] = new Screen;
	}
	cout << "size of Screen " << sizeof(Screen) << endl;
	for(int i=0; i<10; i++) {
		cout << "address of p[" << i << "] " << p[i] << endl;
	}
	return 0;
}
  • The second version is similar to the first version, but added the union optimization.
  • The first two versions of the memory allocation operations are implemented within the object, but write for each object a memory allocation is clearly not realistic, the third version of the process of memory allocation is to be extracted, encapsulated into a single class allocate in this allocate memory contains for each class, and then calls the function allocate the dispensing process is completed.

new handler

When the operator new can not afford to allocate enough memory, a bad_alloc will throw an exception. But before throwing an exception, in fact, the system will call you to set up new_handler function, which is a way of platform C ++ programmers offer.

typedef void(*new_handler);
new_handler set_new_handler(new_handler p)  throw();

Excellent new handler should do two things:

  • Make more memory available
  • Call abort or exit to abort the operation, the program ends.

std::allocator

std :: allocator operating mode

allocator 16 maintains lists free_list [16], each chain maintains a fixed size memory pool from free_list [0] byte maintenance 8, each chain of a multi-list maintenance than 8 bytes. When an application wants to allocate a memory size will be adjusted upward to an integer of 8, and then dispensed from the list.

each memory allocator allocates size * 20 * 2 + RoundUp ( ) , the front half of the list to block 20, the rear memory pool called spare pool, is more dispensing part to the next waiting time distribution and use, from the pool cut the number of out of control forever between 1-20. RoundUp along with the increase of the rounds, each of the plurality of memory allocated, are calculated cumulative applications >> 4 , and then transferred to a multiple of eight.

Dispensing process is explained by way of example: Suppose the system memory 10,000

  • Allocating 32 bytes, this case free_list [3] is empty and the pool is empty, it will allocate 32 * 20 * 2 + RoundUp (0 >> 4) malloc byte by one. Then cut out from the first block to the customer, the remaining blocks 19 to hang free_list [3] on. * 2 is behind the operation to the pool size.
    • Cumulative applications: 1280, pool size: 640
  • Allocating 64 bytes, this case free_list [7] but there are pool is empty, it is directly assigned to it by the pool, the first cut to a customer, hanging the remaining 9 to free_list [7] on.
    • Cumulative applications: 1280, pool size: 0
  • Allocating 96 bytes, then the first process repeated, before dispensing 96 * 20 * 2 + RoundUp (1280 >> 4) bytes and 20 come to free_list [11] use.
    • Cumulative applications: 5200, pool size: 2000
  • Allocating 88 bytes, this case free_list [10] but there are pool is empty, it is assigned from the pool, because the number of blocks allocated from the pool each time between 1 and 20, 20 is assigned to it.
    • Cumulative applications: 5200, pool size: 240
  • Application three consecutive 88 bytes because free_list [10] 19 there is also free, so directly assigned to the customer 3
    • Cumulative applications: 5200, pool size: 240
  • Application 8 bytes, then free_list [0] exists but is empty pool, so 8 bytes allocated 20 directly from the pool to free_list [0] using
    • Cumulative applications: 5200, pool size: 80
  • Application 104 bytes, then free_list [12] are not enough empty but a pool, then the pool 80 assigned to free_list [9], the distribution by malloc 104 * 20 * 2 + RoundUp (5200 >> 4) bytes
    • Cumulative applications: 9688, pool size: 2408
  • Application 112 bytes, then free_list [13] However, the presence of the pool is empty, it is dispensed directly from the pool 20 linked to the block free_list [13] on
    • Cumulative applications: 9688, pool size: 168
  • Application 48 bytes, this case free_list [5] However, the presence of the pool is empty, it is dispensed directly from the pool 3 hanging to free_list [5] on
    • Cumulative applications: 9688, pool size: 24
  • Application 72 bytes, then free_list [8] is not empty but allocated a pool, then the first 24 bytes of the pool linked to free_list [2], and then again by malloc allocation. In this case the system has apparently no extra memory allocation is completed, the system will allocate the allocated resources inside a 72 closest to customers, that is to say at this time will be assigned a 80-byte blocks from free_list [9] and 8 + cut into two portions 72, the first portion allocated to the client, the second portion into pool.
    • Cumulative applications: 9688, pool size: 8
  • Application 120 bytes, then free_list [14] are not enough empty but as a pool, then the first 8 bytes of the pool to hang free_list [0], the case is obviously still not malloc memory usage, but free_list [14] and free_list [15] are empty, then the way it is allocated.
    • Cumulative applications: 9688, pool size: 0

The second stage configuration source code allocator

enum {__ALIGN = 8};							// 小区块的上调边界
enum {__MAX_BYTES = 128};					// 小区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};	// free-list 的个数

class alloc {
private:
	union obj{
		union obj* free_list_link;
	};
	static char* start_free;				// 指向 pool 的头
	static char* end_free;					// 指向 pool 的尾
	static size_t heap_size;				// 累计分配量
	static obj* free_list[__NFREELISTS];	// free_list 数组

	static size_t ROUND_UP(size_t bytes) {	// 将 bytes 向上调整到 8 的倍数
		return ((bytes + __ALIGN-1)) & ~(__ALIGN-1);
	}
	static size_t FREELIST_INDEX(size_t bytes) {	// 获得应该从哪个 free_list 分配内存
		return (bytes+__ALIGN-1)/__ALIGN -1;
	} 
	static void* refill(size_t n) {			// 为链表填充内存,此时的 n 已经是 8 的倍数
		int nobjs = 20;
		char *chunk = chunk_alloc(n, nobjs);
		obj **my_free_list;
		obj *result;
		obj *current_obj;
		obj *next_obj;
		if(1 == nobjs)	return chunk;
		my_free_list = free_list + FREELIST_INDEX(n);
		result = (obj*)chunk;
		*my_free_list = next_obj = (obj*)(chunk+n);		// 直接拿第二块挂到链表上
		for(int i=1; ; i++) {
			current_obj = next_obj;
			next_obj = (obj*)((char*)next_obj+n);
			if(nobjs-1 == i) {
				current_obj->free_list_link = NULL;
				break;
			} else {
				current_obj->free_list_link = next_obj;
			}
		}
		return result;
	}
	static char* chunk_alloc(size_t size, int &nobjs){	// 分配一大块内存
		char *result;
		size_t total_bytes = size*nobjs;
		size_t bytes_left = end_free - start_free;
		if(bytes_left >= total_bytes) {		// 先从 pool 池分配,如果能够满足分配 20 个块
			result = start_free;
			start_free += total_bytes;
			return result;
		} else if(bytes_left >= size) {		// 如果不够 20 个但至少能分配一个
			nobjs = bytes_left/size;
			total_bytes = size*nobjs;
			result = start_free;
			start_free += total_bytes;
			return result;
		} else {							// pool 池不够用
			size_t bytes_to_get = 2*total_bytes+ROUND_UP(heap_size>>4);
			if(bytes_left > 0) {			// 处理 pool 池碎片
				obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
				((obj*)start_free)->free_list_link = *my_free_list;
				*my_free_list = (obj*)start_free;
			}
			start_free = (char*)malloc(bytes_to_get);	// 先分配到 pool 池中
			if(0 == start_free) {			// 如果系统内存不够分配
				obj **my_free_list;
				obj *p;
				for(int i=size; i<=__MAX_BYTES; i += __ALIGN) {	// 往后找一块可以拿来用的内存
					my_free_list = free_list + FREELIST_INDEX(i);
					p = *my_free_list;
					if(0 != p) {			// 如果找到一块可以用的
						*my_free_list = p->free_list_link;
						start_free = (char*)p;
						end_free = start_free+i;
						return chunk_alloc(size, nobjs);// 扩充的 pool,所以在分配一次一定可以分配到
					}
				}
				// 尝试利用一级配置器分配内存
				end_free = 0;
//				start_free = (char*)malloc_alloc::allocate(bytes_to_get);
			}
			heap_size += bytes_to_get;
			end_free = start_free + bytes_to_get;
			return chunk_alloc(size, nobjs);			// 扩充的 pool,所以在分配一次一定可以分配到
		}
	}
	
public:
	static void* allocate(size_t n) {
		obj **my_free_list;					// obj**
		obj *result;
		if(n > (size_t)__MAX_BYTES) {		// 改用第一级p配置器
//			return malloc_alloc::allocate(n);
		}
		my_free_list = free_list + FREELIST_INDEX(n);
		result = *my_free_list;				// 获得对应的链表指针
		if(result == NULL) {				// 如果链表为空
			void *r = refill(ROUND_UP(n));
			return r;
		}
		*my_free_list = result->free_list_link;
		return result;
	}
	static void deallocate(void *p, size_t n) {
		obj *q = (obj*) p;
		obj **my_free_list;					// obj**
		if(n > (size_t)__MAX_BYTES) {		// 改用第一级p配置器
//			malloc_alloc::deallocate(p, n);
			return ;
		}
		my_free_list = free_list + FREELIST_INDEX(n);
		q->free_list_link = *my_free_list;
		*my_free_list = q;
	}
};
char* alloc::start_free = NULL;
char* alloc::end_free = NULL;
size_t alloc::heap_size = 0;
alloc::obj* alloc::free_list[__NFREELISTS] = {0};

Guess you like

Origin www.cnblogs.com/Jiaaaaaaaqi/p/12629901.html