Chapter II Space configurationr (alloctor)
1. Standard interface for space configurations
1.1 General interface (generalized)
allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference_type
1.2 Specialized interface (specified)
Interface | Role |
---|---|
allocator::rebind | The aim is to help alocator to allocate space for different types of elements, even if alocator has been designated as an object to allocate space. |
allocator::allocator() | Default Construct Functions |
allocator::allocator(const allocator&) | Copy Functions |
template<class U>allocator::allocator(const allocator<U>&) | Pan copy function |
allocator::~allocator() | Default Parsing Functions |
pointer allocator::address(reference x) const | returns the address of the specified object, e. g. a.address(x) < = & x |
pointer allocator::allocate(size_type n, const void* = 0) | Configure space to store n-type T objects, the second parameter is primarily used to optimize memory optimization and management |
allocator::deallocate(pointer p, size_type n) | Memory allocated before release |
allocator::allocator::max_size() const | Returns the value of the maximum memory size successfully assigned |
allocator::destroy(pointer p) | Equivalent to p->T() |
In one example.allocator::rebind
Complementing:
template<class T>
class alloc;
template<typename T, typename Alloc = alloc<T>>
class MyContainer{
public:
template<typename U>
struct rebind{
typedef Alloc<U> other; // Allocator type for type U
// other represents some other objects
};
};
C++1.3 Design of a simple space configurer
#include <new> // for placement new
#include <cstddef> // for ptrdiff_t, size_t
#include <cstdlib> // for exit()
#include <climits> // for UINT_MAX
#include <iostream> // for cerr
#include <vector> // for test
namespace T_ {
// allocate memory
template<class T>
inline T* _allocate(ptrdiff_t size, T*) {
std::set_new_handler(0); // invoke when out of memory
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T)))); // allocate memory
if (tmp == 0) { // the allocated memory is 0, which means that allocating memory fails
std::cerr << "out of memory" << '\n';
exit(0); // terminate processs
}
return tmp; // return the pointer of the allocated memory block
}
// release memory
template<class T>
inline void _deallocate(T* buffer) {
::operator delete(buffer); // release the allocated memory
}
// constructor
template<class T1, class T2>
inline void _construct(T1* p, const T2& value) {
new(p) T1(value); // assign value at pointer p
}
// destructor
template<class T>
inline void _destroy(T* ptr) {
ptr->~T(); // invoke the destructor of type class
}
template<class T>
class allocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
/*-----implement part of the specific API-----*/
// allocator::rebind->rebind allocator of type U
template<class U>
struct rebind {
typedef allocator<U> other;
};
// pointer allocator::allocate->hint used for locality
pointer allocate(size_type n, const void* hint = 0) {
return _allocate((difference_type)n, (pointer)0);
}
// allocator::deallocate
void deallocate(pointer p, size_type n) { _deallocate(p); }
// allocator::construct
void construct(pointer p, const T& value) {
_construct(p, value);
}
// allocator::destroy
void destroy(pointer p) { _destroy(p); }
// pointer allocator::address
pointer address(reference x) { return (pointer)&x; }
// const_pointer allocator::const_address
const_pointer const_address(const_reference x) {
return (const_pointer)&x;
}
// size_type allocator::max_size()
size_type max_size() const {
return size_type(UINT_MAX / sizeof(T));
}
};
}
int main() {
int ia[5] = { 0, 1, 2, 3, 4 };
unsigned int i;
std::vector<int, T_::allocator<int>> iv(ia, ia + 5);
for (i = 0; i < iv.size(); i++) std::cout << iv[i] << ' ';
std::cout << '\n';
return 0;
}
C++The STL here is just a simple aloctor.
2. SGI space configurationr
The SSI configuration is different from the normal aloctor
Name | Syntax: | Accept Parameters | Compiler | |
---|---|---|---|---|
GENERAL | Allocator | vector<int, std::allocator<int>> iv | Yes. | VC or VB |
SGI | Alloc | vector<int, sd::alloc> iv | Yes | GCC OR G++ |
Although the GSI aloc is different from the standard alocator, it does not affect its use, as it is generally stated in default.
Each of SGI's containers uses default space configuror aloc, as follows:
template<class T, class Alloc = alloc> // 缺省使用alloc为配置器
class vector{ ... };
C++2.1 SGI standard space configurer std::allocator
There's a name inside SGI.allocator
It's less efficient than a standard space configuror.C++
Yes. ::operator new
and ::operator delete
Simple packaging was performed.
This is SGI_style's.allocator
Achieved.
Note: SGI_styleallocator
Accept onlyvoid*
type.
#include <new>
#include <cstddef>
#include <cstdlib>
#include <limits>
#include <iostream>
#include <algorithm>
#include <vector>
/*-----generalized_style-----*/
template<class T>
inline T* allocate(ptrdiff_t size, T*)
{
std::set_new_handler(0);
T* tmp = (T*)(::operator new ((size_t)(size * sizeof(T))));
if(tmp == 0)
{
std::cerr << "out of memory" << '\n';
exit(1);
}
return tmp;
}
template<class T>
inline void deallocate(T* buffer)
{
::operator delete(buffer);
}
/*-----SGI_style-----*/
template<class T>
class allocator
{
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T* const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
pointer allocate(size_type n)
{
return ::allocate((difference_type)n, (pointer)0);
}
/*-----in the book-----*/
/*void deallocate(pointer p) { ::deallocate(p); }
this deallocate is not runnable */
/*-----and the below "deallocate" is runnable --------------*/
void deallocate(pointer p, size_type n) { ::deallocate(p); }
pointer address(reference x) { return (pointer)&x; }
const_pointer const_address(const_reference x)
{
return (const_pointer)&x;
}
size_type init_page_size()
{
return std::max(size_type(1), size_type(4096 / sizeof(T)));
}
size_type max_size() const
{
return std::max(size_type(1), size_type(UINT_MAX / sizeof(T)));
}
};
int main() {
int ia[5] = { 0, 1, 2, 3, 4 };
unsigned int i;
std::vector<int, allocator<int>> iv(ia, ia + 5);
for (i = 0; i < iv.size(); i++) std::cout << iv[i] << ' ';
std::cout << '\n';
return 0;
}
C++2.2 SGI Special Space Configurer std::alloc
SGI_style.std::alloc
Compared to SGI_style std::allocator
In contrast, the former is allocated/released to grass-roots memory.operator::new
& operator::delete
Efficiency optimization.
Operations in C++ for the configuration and release of the object to memory
class Foo { ... };
Foo* pf = new Foo; // allocate memory, then construct object
delete pf; // destruct object, and release memory
The process for memory configuration and release is as follows:
Phase I | Phase II | |
---|---|---|
Oh, new. | Call::operator new Configure Memory | CallFoo::Foo() Construct Object |
Delete | CallFoo::~Foo() Parsing Objects | Call::operator delete Release Memory |
In short:
- Build a house.
::operator new
- People live inside.
Foo::Foo()
- He's gone.
Foo::~Foo()
- I'll tear down the house.
::operator delete
In STL allocator there is a fine division of the steps above:
alloc::allocate()
Configure Spacealloc::deallocate()
Release Spacealloc::construct()
Construct Objectalloc::destroy()
Parsing Objects
In STL standard, allocator defines<memory>
Medium, SGI<memory>
contains the following two documents:
#include<stl_alloc.h> // for allocating and deallocating of memory
#include<stl_construct.h> // for constructing and destructing of object
C++2.3 Basic tools for construction and characterization: construct()
& destroy()
Code the contents of the above diagram:
I'm sorry, I'm sorry.
#include <new> // for placement new
template<class T1, class T2>
inline void construct(T1* p, const T2& value){
new(p) T1(value); // placement new -> invoke T1::T1(value);
}
C++First editiondestroy()
Specified
template<class T>
inline void destroy(T* pointer){
pointer->~T(); // invoke ~T()
}
C++pointer->~T();
General resolution.
Second editiondestroy()
I don't know.
template<class T>
inline void destroy(ForwardIterator first, ForwardIterator last){
__destroy(first, last, value_type(first));
}
C++__destroy(first, last, value_type(first));
Select the most appropriate way to structure according to the type of element.
Second editiondestroy()
SE
- SE1 (char* ver.) ->
inline void destroy(char*, char*) {}
- SE2 (wchar_t*ver.) ->
inline void destroy(wchar_t*, wchar_t*) {}
To determine whether the type of element existstrivial destructor
I don't know.
template<class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*){
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
}
C++- Element type:
non-trivial destructor
Specified
templace<class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type){
for(; first < last; ++first) destroy(&*first);
}
C++- Element type:
trivial destructor
Specified
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type){}
C++Supplementary and explanatory information:
- The functions used to construct and interpret are global functions (STL Regulation: Configurers must have two member functions called Constract() and Destroy())
- Up there.
construct()
Accept one Pointerp And one. Original value- ♪ Set the beginning to the pointer ♪placement new()
- First edition
destroy()
Accepts a pointer to deconstruct the object to which the pointer refers. Second editiondestroy()
Acceptfirst
andlast
Two intertrajectors for deconstructing all objects within [first, last]* - Before an object is convulsed, consider whether all the convulsive functions of the object are availableI don't know.->
value_type()
- >__type_traits<T>
(Assumption of the contours of this type of objectI don't know.)
4.1 Yes(__true_type)
, directly ignore
4.2 No(__false_type)
, each subject in the rangeDestroy() (first edition)
2.4 Space allocation and release std::alloc
Generally, an object is configured in space before it is constructed, and after the object is constructed, the configured memory is released. In SGI, the processes are as follows:
- General I'm sorry. Request space
- Consider Multi-SystemMulti-threads ) status
- Response measures when inadequate memory is considered
- Consideration of potential memory debris problems that may arise from memory distribution
Memory debris: Different object configurations require different sizes of memory space to be requested, but may result in small memory blocks (part of which does not meet the memory needs of the target object ' s construction) that are not reasonably distributed (with small or large) and that are increasingly available over time, even if the total remaining memory is large enough to allocate space to a large object, thus causing waste of memory.
Possible problems caused by memory debris:
- Available space for fragmentation
- Inefficient use of memory
- Increased probability of distribution failure
- Performance is down.
Possible solutions to the problem of memory debris:
- Use a fixed size memory pool: pre-allocated a fixed size memory block
- Custom memory profiler: memory distribution according to actual needs
- Memory compression: manual removal of memory debris
- Smart Memory Management: RAII and Smart Pointer
- Limiting dynamic memory allocation: use of stack-based memory allocation for small, short-life objects and dynamic memory allocation for large, long-life objects
In C++, configure memory is::operator new()
The release of memory is::operator delete()
。
Category Cmalloc()
andfree()
function.
It's also passed in the SGI.malloc()
andfree()
Two functions to configure and release memory.
Taking into account the problem of memory debris mentioned above, SGI designed a two-tiered memory configuration:
#ifdef
typedef __malloc_alloc_template<0> malloc_alloc;
typedef malloc_alloc alloc; // set alloc as primary allocator
#else
// set alloc as secondary allocator
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
#endif
C++- The first stage configuration is directly used
malloc()
andfree()
- The second-tier configurationr uses different distribution strategies, depending on the situation:
- Use level 1 configuration when RAM blocks greater than 128bytes need to be configured
- Use secondary configuration - > I don't know. (RAMS)
SGI will seal a two-layer and two-layer configuration with one of the following interfaces (to make it STL-compliant):
template<class T, class Alloc>
class simple_alloc{
public:
static T* allocate(size_t n){
return 0 == n ? 0 : (T*) Alloc::allocate(n * sizeof(T));
}
static T* allocate(void){
return (T*) Alloc::allocate(sizeof(T));
}
static void deallocate(T* p, size_t n){
if(0 != n) Alloc::deallocate(p, n * sizeof(T));
}
static void deallocate(T* p){
Alloc::deallocate(p, sizeof(T));
}
};
C++In summary, the member functions within the interface are those within the configuration, where the configuration unit is the size of the target elementsizeof(T)
。
All SGI STL containers are used.simple_alloc
This interface, such as:
template<class T, class Alloc = alloc> // default
class vector{
protected:
// specified allocator that allocates a size of element per time
typedef simple_alloc<value_type, Alloc> data_allocator;
void deallocate(){
if(...) data_allocator(start, end_of_storage - start);
}
...
};
C++Map of the relationship of the first and second configurations
Map of the relationship between the first and second configurations and the packaging interface and mode of operation
2.5 First stage configuration _ Malloc_alloc_template Parsing
#if 0
# include <new>
# define THROW_BAD_ALLOC throw bad_alloc
#elif #defined(__THROW_BAD_ALLOC)
# include <iostream>
# define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1);
#endif
C++特点:
- Slower than default allocator
- It's generally thread-safe, with higher utilization of memory space
Notes:
- No reference template usage
template<>
is defined as the internal type and variable name, which is given directly to the variable at the time of the demonstration.
template<int inst>
class ex{
public:
void printf() { std::cout << "inst: " << inst; }
};
int main(){
/* ex<para> e1; here para = inst */
ex<3> e1;
e1.printf();
return 0;
}
C++static
Modifier
static | Non-static | |
---|---|---|
I don't know. | Accessible | Accessible |
I don't want to see you again, but... | Accessible | I'm sorry. |
♪ This ♪ | I'm sorry. | Accessible |
#include <iostream>
class MyClass{
public:
static void staticFunction(){
std::cout << "Inside staticFunction" << '\n';
}
void nonstaticFunction(){
std::cout << "Inside nonstaticFunction" << '\n';
}
};
int main(){
/* call static function without an instance */
MyClass::staticFunction();
/* class static function from an instance (this is uncommon but valid) */
MyClass obj;
obj.staticFunction();
/* cannot call non-static function without an instance */
MyClass::nonStaticFunction(); /* Error cannot call non-static function
without instance */
/* call non-static function from an instance */
obj.nonstaticFunction();
return 0;
}
C++- Function Pointer Usage
#include <iostream>
int add(int a, int b);
int subtract(int a, int b);
int main(){
int(*operation)(int, int); /* declaration of function pointer */
operation = add; /* assign address of add function to operation */
printf("Addition result: %d\n", (*operation)(5, 3)); /* call function through function pointer */
operation = subtract; /* assign address of subtract function to operation */
printf("Subtract result: %d\n", (*operation)(5, 3)); /* call function through function pointer */
return 0;
}
int add(int a, int b){ return a + b; }
int subtract(int a, int b) { return a - b; }
C++Level 1 Configurer
template<int inst>
class __malloc_alloc_template {
private:
/* the below are the function
pointers which are used for cope with the out-of-memory(oom) */
static void *oom_malloc(size_t);
static void *oom_realloc(void *, size_t);
static void (* __malloc_alloc_oom_handler)();
public:
/* allocator */
static void *allocate(size_t n) {
void *result = malloc(n); // primary allocator employs malloc() directly
/* when out of memory, invoke oom_malloc() */
if (0 == result) result = oom_malloc(n);
return result;
}
/* deallocator */
static void deallocate(void *p, size_t /* n */) {
free(p); /* primary allocator employs free() directly */
}
/* reallocator */
static void *reallocate(void *p, size_t /* old_sz */, size_t new_sz) {
void *result = realloc(p, new_sz);
/* primary allocator employs realloc() directly
when out of memory, invoke oom_realloc()*/
if (0 == result) result = oom_realloc(p, new_sz);
return result;
}
/* emulate set_new_handler(), which means that
out-of-memory handler is definable */
static void (*set_malloc_handler(void (*f)()))() {
void (* old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return (old);
}
};
/* malloc_alloc out-of-memory banding */
template<int inst>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
template<int inst>
void* __malloc_alloc_template<inst>::oom_malloc(size_t n){
void (* my_malloc_handler)();
void* result;
for(;;){ /* endlessly allocate and deallocate until allocate successfully */
my_malloc_handler = __malloc_alloc_oom_handler;
if(0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
(* my_malloc_handler)();
result = malloc(n);
if(result) return result;
}
}
C++Notes: typedef __malloc_alloc_template<0> malloc_alloc;
Here.inst
The value is designated zero.
The first-stage configuration is mainly used C Mediummalloc()
, free()
, realloc()
function to configure, release, reconfigure and imitate memory C++ Yes.new-handler
Mechanisms (because they are not directly used here) ::operator new
Configure memory)
在 C++ Medium new handler
: When the system is unable to meet memory configuration needs, you can call a function you specify.
In other words, when operator::new
When the memory configuration operation cannot be completed, a self-defined function is called before dropping an anomaly new-handler It is a compromise to solve the problem of memory deficiencies.
Notes:
- SGIMedium
malloc
Not::operator new
To configure memory, a similar need needs to be achieved C++ Mediumset_new_handler()
functionset_malloc_handler()
。 - SGI's first-degree configuration.
allocate()
andrealloc()
It's all on call.malloc()
andrealloc()
Could not close temporary folder: %soom_malloc()
andoom_realloc()
, and then try to recycle and release memory until memory configuration is successful, if memory configuration is not successful, thenoom_malloc()
andoom_realloc()
It'll throw out.__THROW_BAD_ALLOC
Yes. Bad_allocUnusual information, or directexit(1)
Suspension of proceedings. - The processing function for memory deficiencies is performed by the programme designer.
2.6 Second stage configuration_default_alloc_templateParsing
The secondary configurationr has additional mechanisms than the first-stage configuration, which reduces the amount of memory waste (RAM debris) associated with small memory distributions and makes subsequent memory space management more difficult because the system always relies on this additional space for memory management.
Memory distribution mechanism for the SGI-II configurationr:
- When the assigned memory blocks are large enough (greater than 128bytes), they are transferred to the first-stage configuration.
- When the amount of memory blocks allocated is smaller (less than 128bytes), it is managed through memory pool - sub-level management
Level approach
Each time you configure a large memory and maintain the corresponding free-lists. Next time you have the same size memory needs, distribute them directly from the free-lists, and if the program releases small memory blocks, then recycle them from the configuration to the free-lists.
In order to facilitate management, SGI ' s secondary configuration will proactively increase any small memory requirements up to a multiple of 8 and maintain 16 free-lists of 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 bytes.
free-lists
The structure is as follows:
union obj{
union obj* free_list_link;
char client_data[1];
};
C++Here, union obj* free_list_link
Comparable to chain LinkList* next
, char client_data[1]
In turn, it points to actual memory blocks, which saves the costs of memory.
Add:
struct
Each member variable has its own memory space.union
Each member variable shares a memory space
Part of the secondary configuration
#include <cstddef>
enum {__ALIGN = 8};
enum {__MAX_BYTES = 128};
enum {__NFREELISTS = __MAX_BYTES / __ALIGN };
template<bool threads, int inst>
class __default_alloc_template{
private:
static size_t ROUND_UP(size_t bytes){ return (((bytes) + __ALIGN - 1) & ~(__ALIGN - 1)); }
/* '(bytes + __ALIGN -1': This part adds an offset to the 'bytes' value. The '__ALIGN - 1'
* is used to ensure that we round up to the next multiple of '__ALIGN'.For example, if '__ALIGN'
* is 4, then '__ALIGN - 1' is 3, and adding 3 ensures that the result will be at least a multiple
* of 4 greater than 'bytes'.
* '& !(__ALIGN - 1)‘: This part performs a bitwise AND operation wit the complement of '(__ALIGN - 4)'.
* The complement operation '~' flips all the bits of '(__ALIGN - 1)', effective creating a bitmask where
* all bits are set to 1 except for the lower bits determined by '__ALIGN - 1'. By performing a bitwise AND
* with this bitmask, we effectively set the lower bits to O, thus rounding down the result to the nearest
* multiple of '__ALIGN'.
*/
private:
union obj{
union obj* free_list_link;
char client_data[1];
};
private:
/* 16 free-lists */
static obj* volatile free_list[__NFREELISTS];
/* employ Nth free-list according to the size
* of memory chunk (begin with 1st memory chunk)
*/
static size_t FREELIST_INDEX(size_t bytes){ return (((bytes) + __ALIGN + 1) / __ALIGN - 1); }
/* return a object in size of n, and add other memory
* chunks in size of n into free-list if possible
*/
static void* refill(size_t n);
/* allocate a memory space that can hold n objs
* its capacity is size
* if inconvenient to allocate for n objs, n cloud decrease
*/
static void* refill(size_t size, int& nobjs);
// chunk allocation state
static char* start_free; // the start position in memory pool, only changes in chunk_alloc()
static char* end_free; // the end position in memory pool, only changes in chunk_alloc()
static size_t heap_size;
public:
static void* allocate(size_t n) { }
static void deallocate(void* p, size_t n) { }
static void* reallocate(void* p, size_t old_sz, size_t new_sz);
};
/* the original value and definition setting for static data member */
template <bool threads, int inst>
char* __default_alloc_template<threads, inst>::start_free = 0;
template <bool threads, int inst>
char* __default_alloc_template<threads, inst>::end_free = 0;
template <bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;
template <bool threads, int inst>
__default_alloc_template<threads, inst>::obj* volatile
__default_alloc_template<threads, inst>::free_list[__NFREELISTS] =
{0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0};
C++2.7 Space Configuration Functionsallocate()
Configurer ' s standard interface function alocate() works as follows:
- judge the size of blocks
- More than 128bytes Call Level 1 Configurer
- Less than 128bytes check the corresponding free list
- If there are available blocks in free list, use them directly.
- If there are no available blocks in the free list, increase the size of the block to eight times bytes and call refill() to refill the space for free list
// n must be > 0, to assure that
// there is the memory allocated
static void* allocate(size_t n){
obj* volatile* my_free_list;
obj* result;
// if allocating memory > 128 bytes
if(n > (size_t) __MAX_BYTES){
return (malloc_alloc:allocate(n));
}
// select a suitable chunk in 1 of 16 free lists
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if (result == 0){
// no find a available free list
// preparing to refill another
void* r = refill(ROUND_UP(n));
return r;
}
// adjust free list
*my_free_list = result -> free_list_link;
return (result);
};
C++Operation where memory blocks are removed from free list
2.8 Space Release Functiondeallocate()
__default_alloc_template has a standard interface function deallocate() which works as follows:
- judge the size of blocks
- More than 128bytes, direct to first-level configuration
- Less than 128bytes, find the corresponding free list and recycle blocks
// p can not be 0
static void deallocate(void* p, size_t n){
obj* q = (obj *)p;
obj* volatile* my_free_list;
// if size > 128 bytes, call the primary allocator
if(n > (size_t) __MAX_BYTES){
malloc_alloc::deallocate(p, n);
return;
}
// selecting the corresponding free list
my_free_list = free_list + FREELIST_INDEX(n);
// adjust free list, recycle the chunk
q -> free_list_link = *my_free_list;
*my_free_list = q;
}
C++Memory block recovery to free list
2.9 Refillfree lists
When free lists do not have available blocks, call refill() as free list refill space, new space removed from memory pool (completed by chunk_alloc()):
- When the remaining memory in the memory pool was sufficient, 20 new blocks were acquired by default
- When the remaining memory in the memory pool is insufficient, the number of new blocks obtained may be less than 20
// return a obj in size of n
// and supply some chunks for free list properly sometimes
// supposing that n is upregulated to a multiple of 8
template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n){
int nobjs = 20;
// call chunk_alloc(), try to get n(objs) chunks
// as new nodes for free list
char* chunk = chunk_alloc(n, nobjs); // nobjs -> pass by reference
obj* volatile* my_free_list;
obj* result;
obj* current_obj, * next_obj;
int i;
// if only get one chunk, then allocate this chunk to the target
// so there is no new node for free list
if(l == nobjs) return(chunk);
// if not, adjust free list and prepare to link a new node
my_free_list = free_list + FREELIST_INDEX(n);
// create free list in chunk space
result = (obj*)chunk; // this piece of chunk will return to user
// help free list point toward the new allocated space (from memory pool)
*my_free_list = new_obj = (obj*)(chunk + n);
// connect the each node in free list
for(i = 1; ; i++){ // i begin with 1, because i[0] will be return to user
current_obj = next_obj;
if (nobjs - 1 == i){
current_obj -> free_list_link = 0;
break;
}else{
current_obj -> free_list_link = next_obj;
}
}
return(result);
}
C++2.10 Memory poolmemory pool
Speaking of...refill()
. . . . . . . . .chunk_alloc()
This function, it's used to extract space from the memory pool to giveFree list。
template <bool threads, int inst>
char* __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs){
char* result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free; // the rest of memory pool
if(bytes_left >= total_bytes){
// the rest of memory pool could satisfy the need
result = start_free;
start_free += total_bytes;
return(result);
}else if( bytes_left >= size){
// the rest of memory pool could not totally satisfy the need
// but only one or more chunks
nobjs = bytes_left / size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return(result);
}else{
// memory pool can not supply the size o only one chunk
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
// make full use of memory fragments
if(bytes_left > 0){
// allocate some surplus memory for free list primarily
// select the proper free list
obj* volatile* my_free_list = free_list + FREELIST_INDEX(bytes_left);
// adjust free list, integrate the surplus memory in memory pool
((obj*) start_free) -> free_list_link = *my_free_list;
*my_free_list = (obj*) start_free;
}
// configure heap to supply memory pool
start_free = (char*) malloc(bytes_to_get);
if(0 == start_free){
// heap is not enough, malloc() fails
int i;
obj* volatile* my_free_list, *p;
// check the memory that I currently have
// allocate the small memory space is dangerous under multi-threads circumstance
// select a proper free list
// "proper" means that the unused memory is big enough to satisfy the need
for(i = size; i <= __MAX_BYTES; i += __ALIGN){
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if(0 != p){
// chunk not in free list
// adjust free list to release the unused memory
*my_free_list = p -> free_list_link;
start_free = (char*) p;
end_free = start_free + i;
// revoke itself to fix nobjs
return(chunk_alloc(size, nobjs));
// any surplus memory will be integrated in free list as spare
}
}
end_free = 0; // if occur the accident, allocate the primary allocator
// turning to out-of-memory
start_free = (char*)malloc_alloc::allocate(bytes_to_get);
// if failed, throw exception
// if succeeded, memory lack will be fixed
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
// revoke itself to fix nobjs
return(chunk_alloc(size, nobjs));
}
C++chunk_alloc()
Function passesend_free - start_free
To determine the memory balance in memory:
- If there's enough memory, we'll move 20 blocks back.Free list
- If memory is insufficient
- But if one or more blocks are satisfied, then the remaining blocks will be transferred.
- I can't even move a block out, and I'm trying to use it.
malloc()
FromHeapMiddle Configure Memory to Increase Memory Residues in Memory Pools, and the size of the Configuration Memory has gradually increased with the number of configurations
Examples:
- When Call
chunk_alloc(32, 20)
♪ And then ♪malloc()
Configure 40 32bytes blocks, remove the first of these blocks and release the remaining 19free_list[3]
Carry out maintenance and leave the remaining 20 to the memory pool. - When called again
chunk_alloc(64, 20)
Time, now.free_list[7]
Empty, and 20 * 32 bytes / 64 bytes = 10, return the 10 blocks, remove the first of them and release the remaining nine.free_list[7]
Central maintenance. The memory pool is empty at this time. - Keep calling.
chunk_alloc(96, 20)
At this point,free_list[11]
It's empty and it's time to look for memory from the memory pool, but it's also empty, so there'smalloc()
Configure 40 + n (additional) 96bytes blocks, remove the first block of the block and hand 19 tofree_list[11]
Maintain the remaining 20+n (additional) to the memory pool ... - ♪ When the wholeI'm sorry.There's so little space to add memory to the memory pool.
malloc()
It is not impossible to continue.chunk_alloc()
Again.Free listFinds if there are available and large enough blocks, if available, or if there is no first-level configuration, use the first-level configuration.malloc()
Make memory configuration, but it hasOut-of-memoryTreatment mechanism (similar tonew handler
):- If successful, release enough memory to use
- If you fail, throw.
bad_alloc
Unusual
simple_alloc to provide a standard configuration interface
template<class T, class Alloc>
class simple_alloc{
public:
static T* allocate(size_t n){
return 0 == n ? 0 : (T*) Alloc::allocate(n * sizeof(T));
}
static T* allocate(void){
return (T*) Alloc::allocate(sizeof(T));
}
static void deallocate(T* p, size_t n){
if(0 != n) Alloc::deallocate(p, n * sizeof(T));
}
static void deallocate(T* p){
Alloc::deallocate(p, sizeof(T));
}
};
C++SGI usually uses this way to use the configuration:
template <class T, class Alloc = alloc> // 缺省使用 alloc 为配置器
class vector{
public:
typedef T value_type;
protected: // 专属配置器,每次配置一个元素大小
typedef simple_alloc<value_type, Alloc> data_allocator;
};
C++2.11 Memory Basic Processing Tool
STL defines five global functions that function in uninitialized spaces:
construct()
- > Constructdestroy()
- I'm sorry.uninitialized_copy()
->copy()
uninitialized_fill()
->fill()
uninitialized_fill_n()
->fill_n()
of which copy()
, fill()
, fill_n()
ForSTLAlgebra
uninitialized_copy()
template<class InputIterator, class ForwardIterator>
ForwardIterator
unitialized_copy(InputIterator first, InputIterator last,
ForwardIterator result);
C++uninitialized_copy()
Dissociate the memory configuration from the structure of the object, working in the working mode: within the input range, using an iterative devicei
, the anecdote will be called construct(&*(result + (i - first)), *i)
, which produces*i
, and place the object to which the copy is given at the relative position of the output range.
When it comes to a container, uninitialized_copy()
The tectonic function of the packaging over the memory block after it has configured the memory block (in order to include all the elements within it).
C++ has another requirement for this:I don't know what to do with it. : If it works for every elementuninitialized_copy()
, thenI don't know. Or elseRollback。
uninitialized_fill
template<class ForwardIterator, class T>
void unintialized_fill(ForwardIterator first, ForwardIterator last,
const T&x);
C++uninitialized_fill()
Dissociate the memory configuration from the structure of the object, working in the same way: for each iterative in the input rangei
Call construct(&*i, x)
Yes.i
Place a copy of x where it refers.
C++ has the same thing.I don't know what to do with it.Requirements: If every element can be successfully implementeduninitialized_fill()
, thenI don't know. , if there's oneCopy constructorThrows an anomaly (operating failure) and disassembles all successful elements.
uninitialized_fill_n
template<class ForwardIterator, class Size, class T>
ForwardIterator
uninitialized_fill_n(ForwardIterator first, Size n, const T& x);
C++uninitialized_fill_n()
Separates the memory configuration from the structure of the object, which sets the same opening value for all elements in the specified range. Yes.[first, first + n]
Every iterative in rangei
, uninitialized_fill_n()
Callconstruct(&*i, x)
It's created in the corresponding position.x
.
C++ has the same thing.I don't know what to do with it.Request: If it succeeds for every elementuninitialized_fill_n()
, thenI don't know. , if there's oneCopy constructorThrows an anomaly (operating failure) and disassembles all successful elements.