Explain the space configurator in STL (SGI version)

Keywords: less

Space Configurator

1. What is a space Configurator

Efficient management of space (space application and recycling) for each container

2. Why do I need a space Configurator

Various containers - > can store elements - > space is needed at the bottom

new application space
  1. operator new ---->malloc
  2. Call constructor ------ complete object construction
Summary of dynamic memory management

In the front container, new is used for every space opening, but there are some disadvantages in using new

  • Space application and release need to be managed by users themselves, which is easy to cause memory leakage
  • Frequently apply to the system for small blocks of memory, which is easy to cause memory fragmentation, such as: nodes
  • Frequent application of small block of memory to the system affects the running efficiency of the program
  • Directly use malloc and new to apply, there is extra space waste before each space (malloc will add new things before and after the space applied)
  • How to deal with space application failure
  • The code structure is confusing and the code reuse rate is not high
  • Thread safety not considered

Efficient memory management

3. How to implement SGI – STL space Configurator

Small block memory
  1. Greater than 128 bytes ----- > large memory
  2. 128 bytes or less
  • The primary space configurator handles large blocks of memory,
  • The secondary space configurator processes small blocks of memory.

Primary space Configurator



Application space
void * allocate(size_t n)
	// Apply for space successfully, return directly, fail to be handled by oom ﹣ malloc
	void * result =malloc(n);
	if(nullptr == result)
		result = oom_malloc(n);
	return result;
Release space
static void deallocate(void *p, size_t /* n */)
The processing of "oom" malloc after malloc failure
  1. Accept function pointer (call set ﹣ new ﹣ handle)
  2. Verify that the function pointer is empty
    • Yes: throw exceptions directly
  3. Call the function corresponding to the function pointer
  4. Call malloc to continue to apply for space
    • Successful application: direct return
    • Application failed: cycle continues
template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
	void (* my_malloc_handler)();
	void *result;
	for (;;)
		// Check whether the user has set countermeasures for insufficient space. If not, throw exceptions and mode new
		my_malloc_handler = __malloc_alloc_oom_handler;
		if (0 == my_malloc_handler)
		// If set, implement the measures to deal with insufficient space provided by users
		// Continue to apply for space, you may succeed in applying
		result = malloc(n);
		if (result)

Return value type and parameter type are void * () function pointers

// The parameter of this function is a function pointer, and the return value type is also a function pointer
// void (* set_malloc_handler( void (*f)() ) )()
static void (* set_malloc_handler(void (*f)()))()
	void (* old)() = __malloc_alloc_oom_handler;
	__malloc_alloc_oom_handler = f;

Secondary space Configurator

Defects caused by frequent application of small memory blocks to the system
SGI-STL uses the technology of memory pool to improve the speed of application space and reduce the waste of extra space, and uses hash bucket to improve the speed and efficient management of space acquisition.

Memory pool

The memory pool is: first, apply for a larger memory block for standby. When memory is needed, go directly to the memory pool. When there is not enough space in the pool, then go to the memory to get it. When the user does not use it, return it directly to the memory pool. It avoids the problems of low efficiency, memory fragmentation and extra waste caused by frequent application of small memory to the system

char* _start,*finish

Application space
  1. Find the right block in the returned memory block
  2. Find - > split required - > no need - > assign directly
    Need to partition the memory block
  3. Not found ----- > apply to memory pool
Defects in application space
  1. The chain table searches for the right memory block, needs to traverse, and is inefficient
  2. Division
Pay attention to problems
  1. When users need space, can they directly intercept from the large space in the memory pool? Why?
    A: choose from the list first and take from the large blocks first. If users need large blocks of space, they may not be able to give it
  2. Can the space returned by users be directly spliced in front of large memory?
    Answer: No.
  3. How to manage the space returned by users?
    A: linked with a linked list
  4. What are the consequences of continuous cutting?
    A: memory fragmentation

Design of secondary space Configurator

The secondary space configurator in SGI-STL uses the memory pool technology, but it does not manage the space returned by users in the way of linked list (because the efficiency of searching the appropriate small memory is relatively low when users apply for space), but it uses the hash bucket method to achieve higher management efficiency. Do you need 128 barrels of space to manage the memory blocks returned by users? The answer is no, because the space that the user applies for is almost an integral multiple of 4, and the space of other sizes is rarely used. Therefore, SGI-STL aligns the memory block requested by the user upward to an integer multiple of 8

Multiple of 4 for 32-bit system
Multiple of 8 for 64 bit system

There must be a pointer in each linked list. The 32-bit pointer is 4 bytes, and the 64 bit pointer is 8 bytes

template <int inst>
class __default_alloc_template
	enum { __ALIGN = 8 }; // If the memory required by the user is not an integral multiple of 8, align up to an integral multiple of 8
	enum { __MAX_BYTES = 128 }; // Dividing line of size memory block
	enum { __NFREELISTS = __MAX_BYTES / __ALIGN }; // The number of buckets needed to store small pieces of memory with hash buckets
	// If the memory block required by the user is not an integral multiple of 8, align up to an integral multiple of 8
	static size_t ROUND_UP(size_t bytes)
		return (((bytes)+__ALIGN - 1) & ~(__ALIGN - 1));
	// Use consortia to maintain the structure of linked list - students can think about why there is no structure here
	union obj
		union obj * free_list_link;
		char client_data[1]; /* The client sees this. */
	static obj * free_list[__NFREELISTS];
	// Hash function to find the corresponding bucket number according to the number of bytes provided by the user
	static size_t FREELIST_INDEX(size_t bytes)
		return (((bytes)+__ALIGN - 1) / __ALIGN - 1);
	// Start ﹣ free and end ﹣ free are used to mark the start and end positions of large blocks of memory in the memory pool
	static char *start_free;
	static char *end_free;
	// Used to record how many memory blocks the space configurator has requested from the system
	static size_t heap_size;
	// ... cross platform operation, encapsulation lock, application space mode, etc

Management space of secondary space Configurator

void allocate(size_t n)
; / / call the primary space Configurator
1. Calculate the barrel number with n
2. Check whether there is node (internal block) in the bucket
Yes: use the header delete method to return the first memory block
No: return return return (round Fu up (n));

Void refer (size_t / is already an integral multiple of 8 /)
size_t nobjs =20;
char* chunk = chunk_alloc(n,nobjs);
if(nobjs == 1) / / only one piece is needed
return chunk;
Save the first memory, and finally return it to an external user
Hang the remaining nobjs-1 memory block to the corresponding bucket

void * chunk_alloc(size_t size,size_t& nobjs//20)
size_t totalBytes = nobjs*size;
size_t leftBytes = finish-start;

void * res =null;
if(leftBytes > = totalBytes) //Enough memory pool space to provide 20
{res = start;start+=totalBytes;return res;}
else if(leftBytes>=size)//Less than 20, at least 1
{nobjs =leftBytes/size;res=start;start+=nobjssize;return res;}//How many pieces can be provided
else //There is not enough space in the memory pool, not even one piece can be provided
//1. Hang the remaining memory in the memory pool - > to the corresponding bucket
//total_get = 2total_bytes+RoundUP(heap_size>>4);

  1. Application space
// Function function: request space from space Configurator
// Parameter n: number of bytes of space required by the user
// Return value: returns the first address of the space
static void * allocate(size_t n)
	obj * __VOLATILE * my_free_list;
	obj * __RESTRICT result;
	// Detect that the space required by the user is released more than 128 (that is, whether it is small memory or not)
	if (n > (size_t)__MAX_BYTES)
		// It's not a small block of memory, it's handled by the primary space configurator
		return (malloc_alloc::allocate(n));
	// Find the corresponding bucket number according to the bytes required by the user
	my_free_list = free_list + FREELIST_INDEX(n);
	result = *my_free_list;
	// If there is no memory block in the bucket, add space to the bucket
	if (result == 0)
		// Align n up to the integer quilt of 8 to ensure that when the memory block is replenished to the bucket, the memory block must be an integer multiple of 8
		void *r = refill(ROUND_UP(n));
		return r;
	// Maintain the chain relationship of the remaining memory blocks in the bucket
	*my_free_list = result->free_list_link;
	return (result);
  1. Recycling space

    No free space in secondary space Configurator
// Function function: user returns space to space Configurator
// Parameter: total space size of the first address n of p space
static void deallocate(void *p, size_t n)
	obj *q = (obj *)p;
	obj ** my_free_list;
	// If the space is not a small block of memory, it should be recycled by the primary space configurator
	if (n > (size_t)__MAX_BYTES)	//More than 128, released according to the primary space configuration
		malloc_alloc::deallocate(p, n);
	//Not until 128
	// Find the corresponding hash bucket and hang the memory in the corresponding hash bucket
	my_free_list = free_list + FREELIST_INDEX(n);
	q->free_list_link = *my_free_list;
	*my_free_list = q;
  1. Replenish memory

template <int inst>
char* __default_alloc_template<inst>::chunk_alloc(size_t size, int& nobjs)
	// Calculate the total size of nobjs size byte memory block and the total size of the remaining space in the memory pool
	char * result;
	size_t total_bytes = size * nobjs;
	size_t bytes_left = end_free - start_free;
	// If the memory pool can provide a total of bytes, return
	if (bytes_left >= total_bytes)
		result = start_free;
		start_free += total_bytes;
	else if (bytes_left >= size)
		// nobjs block cannot be provided, but at least one size byte memory block can be provided, and it will return
		nobjs = bytes_left / size;
		total_bytes = size * nobjs;
		result = start_free;
		start_free += total_bytes;
		// Insufficient memory pool space, not even a small village
		// Ask the system heap for help and replenish the memory pool
		// Calculate the size of supplementary space to memory: double the total size of this space + apply to the system for the total size / 16
		size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
		// If the memory pool has space left (the space must be an integer multiple of 8), hang the space to the corresponding hash bucket
		if (bytes_left > 0)
			// Find the right hash bucket and hang the remaining space on it
			obj ** my_free_list = free_list + FREELIST_INDEX(bytes_left);
			((obj *)start_free)->free_list_link = *my_free_list;
			*my_ree_list = (obj *)start_free;
		// Replenish space to the memory pool through the system heap. If the replenishment is successful, recursion continues to allocate
		start_free = (char *)malloc(bytes_to_get);
		if (0 == start_free)
			// Failed to replenish the space through the system heap. Check whether there is a large memory block in the hash bucket
			int i;
			obj ** my_free_list, *p;
			for (i = size; i <= __MAX_BYTES; i += __ALIGN)
				my_free_list = free_list + FREELIST_INDEX(i);
				p = *my_free_list;
				// If yes, add the memory block to the memory pool, and continue to allocate recursively
				if (0 != p)
					*my_free_list = p->free_list_link;
					start_free = (char *)p;
					end_free = start_free + i;
					return(chunk_alloc(size, nobjs));
			// The mountain and the water are exhausted. You can only ask the first level space configurator for help
			// Note: you must leave end ﹣ free blank here, because once the primary space configurator throws an exception, there will be problems
			end_free = 0;//End? Free marks the end of memory pool space
			start_free = (char *)malloc_alloc::allocate(bytes_to_get);
		// Replenish space to memory pool through system heap succeeded, update information and continue allocation
		heap_size += bytes_to_get;
		end_free = start_free + bytes_to_get;
		return(chunk_alloc(size, nobjs));

Default selection of space Configurator

By default, SGI-STL uses the primary or secondary space configurator, which is controlled through the use? Malloc macro:

#ifdef __USE_MALLOC
	typedef malloc_alloc alloc;
	typedef malloc_alloc single_client_alloc;
	// Secondary space configurator definition

This macro is not defined in SGI STL, so SGI STL uses the secondary space configurator by default

Re encapsulation of space Configurator

In C + +, the space required by users may be of any type, with single object space and continuous space. It is not very friendly to let users calculate the total space required by themselves every time, so SGI-STL re encapsulates the space configurator

template<class T, class Alloc>
class simple_alloc
	// Request space of n objects of type T
	static T *allocate(size_t n)
		return 0 == n ? 0 : (T*)Alloc::allocate(n * sizeof (T));
	// Request a space of T type object size
	static T *allocate(void)
		return (T*)Alloc::allocate(sizeof (T));
	// Free space of n objects of type T
	static void deallocate(T *p, size_t n)
		if (0 != n)
			Alloc::deallocate(p, n * sizeof (T));
	// Free a space of T type object size
	static void deallocate(T *p)
		Alloc::deallocate(p, sizeof (T));

Object construction and release

For efficiency reasons, SGI-STL decided to separate the process of space application release and object construction and analysis, because some objects do not need to call destructors for construction, and destructors are not needed for destruction, which can improve the performance of the program

// When returning space, first call this function to clean up the resources in the object
template <class T>
inline void destroy(T* pointer)
// After the space application is good, call the function: use placement-new to complete the object's construction.
template <class T1, class T2>
inline void construct(T1* p, const T2& value)
	new (p)T1(value);
235 original articles published, 25 praised, 20000 visitors+
Private letter follow

Posted by Immortal55 on Tue, 14 Jan 2020 19:42:20 -0800