前提阐明

留神比照 G2.9 #207 和 G4.9 #97 行, 新版本中,应用 ::operator new 进行内存申请,意味着咱们能够对其进行重载后做一些察看统计

G2.9 chunk_alloc 的实现

留神:start_free = (char *)malloc(bytes_to_get);

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;    if (bytes_left > total_bytes)    {        result = start_free;        start_free += total_bytes;        return result;    }    else if (bytes_left >= size)    {        nobjs = bytes_left / size;        total_bytes = size * nobjs;        result = start_free;        start_free += total_bytes;        return result;    }    else    {        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);        if (bytes_left > 0)        {            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);        if (0 == start_free)        {            int i;            obj **my_free_list;            obj *p;            for (i=size+__ALIGN; 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);                }            }        }        heap_size += bytes_to_get;        end_free = start_free + bytes_to_get;        return chunk_alloc(size, nobjs);    }}
G4.9 chunk_alloc 的实现

留神:_S_start_free = static_cast<char*>(::operator new(__bytes_to_get));

char *__pool_alloc_base::_M_allocate_chunk(size_t __n, int& __nobjs){    char* __result;    size_t __total_bytes = __n * __nobjs;    size_t __bytes_left = _S_end_free - _S_start_free;        if (__bytes_left >= __total_bytes)    {        __result = _S_start_free;        _S_start_free += __total_bytes;        return __result ;    }    else if (__bytes_left >= __n)    {        __nobjs = (int)(__bytes_left / __n);        __total_bytes = __n * __nobjs;        __result = _S_start_free;        _S_start_free += __total_bytes;        return __result;    }    else    {        // Try to make use of the left-over piece.        if (__bytes_left > 0)        {            _Obj* volatile* __free_list = _M_get_free_list(__bytes_left);            ((_Obj*)(void*)_S_start_free)->_M_free_list_link = *__free_list;            *__free_list = (_Obj*)(void*)_S_start_free;        }                size_t __bytes_to_get = (2 * __total_bytes                                 + _M_round_up(_S_heap_size >> 4));        __try        {            _S_start_free = static_cast<char*>(::operator new(__bytes_to_get));        }        __catch(const std::bad_alloc&)        {            // Try to make do with what we have.  That can't hurt.  We            // do not try smaller requests, since that tends to result            // in disaster on multi-process machines.            size_t __i = __n;            for (; __i <= (size_t) _S_max_bytes; __i += (size_t) _S_align)            {                _Obj* volatile* __free_list = _M_get_free_list(__i);                _Obj* __p = *__free_list;                if (__p != 0)                {                    *__free_list = __p->_M_free_list_link;                    _S_start_free = (char*)__p;                    _S_end_free = _S_start_free + __i;                    return _M_allocate_chunk(__n, __nobjs);                    // Any leftover piece will eventually make it to the                    // right free list.                }            }            // What we have wasn't enough.  Rethrow.            _S_start_free = _S_end_free = 0;   // We have no chunk.            __throw_exception_again;        }        _S_heap_size += __bytes_to_get;        _S_end_free = _S_start_free + __bytes_to_get;        return _M_allocate_chunk(__n, __nobjs);    }}

察看 G2.9 : start_free = (char *)malloc(bytes_to_get);G4.9 : _S_start_free = static_cast<char*>(::operator new(__bytes_to_get));

G4.9 内存调配应用 ::operator new, 意味着咱们能够对重载以察看统计内存申请状况

G4.9 pool allocator 进行察看

设计一个可累计统计调配量和开释量的 operator new/delete
除非用户间接应用 malloc/free, 否则都避不开它们,这样就能够累计总量

根底代码

#include <iostream>#include <string>#include <list>#include <vector>#include<cstdlib>using std::cout;using std::endl;using std::string;using std::list;using std::vector;static long long countNew = 0;static long long countDel = 0;static long long countArrayNew = 0;static long long countArrayDel = 0;static long long timesNew = 0;void *operator new(size_t size){    cout << "operator new, \t" << size << "\n";    countNew += size;    timesNew ++;    return malloc(size);}void *operator new[](size_t size){    cout << "operator new[], \t" << size << "\n";    countArrayNew += size;    timesNew ++;    return malloc(size);}// 一下 (1)(2)能够并存并由(2)抓住流程(但对这的测试无用)// 当只存在 (1) 时,任无奈扎住流程// 在 class members 中二者只能选一 (任一均可)// (1)void operator delete(void *ptr, size_t size){    cout << "operator delete(ptr, size), \t" << ptr << "\n";    countDel += size;    free(ptr);}// (2)void operator delete(void *ptr){    cout << "operator delete(ptr), \t" << ptr << "\n";    free(ptr);}// (1)void operator delete[](void *ptr, size_t size){    cout << "operator delete[](ptr, size), \t" << ptr << "\n";    countArrayDel += size;    free(ptr);}// (2)void operator delete[](void *ptr){    cout << "operator delete[](ptr), \t" << ptr << "\n";    free(ptr);}

func1

void func1(){    cout << "::countNew= " << ::countNew << endl;    cout << "::countDel= " << ::countDel << endl;    cout << "::timesNew= " << ::timesNew << endl;    string *p = new string("My name is Ace");    delete p;    cout << "::countNew= " << ::countNew << endl;    cout << "::countDel= " << ::countDel << endl;    cout << "::timesNew= " << ::timesNew << endl;}

输入:

::countNew= 0::countDel= 0::timesNew= 0operator new,   4                    // string sizeoperator new,   27                   // string(REP) + extra operator delete(ptr),   0xc92380operator delete(ptr),   0xc956b0::countNew= 31                       // 4 + 27::countDel= 0                        // 本测试永远察看不到想要的后果,因为进不去 operator delete(pre, size) 版本::timesNew= 2                       

func2

void func2(){    string *p = new string[3];        delete[] p;        cout << "::countNew= " << ::countNew << endl;    cout << "::countArrayNew= " << ::countArrayNew<< endl;    cout << "::timesNew= " << ::timesNew << endl;}

输入:

operator new[],         16                     // 其中蕴含 arraySize filed: 4 bytes. 所以 16 - 4 = 12 ==> 4 * 3, 也就是三个 string 每个占 4 bytesoperator new,   13                             // Nil stringoperator new,   13                             // Nil stringoperator new,   13                             // Nil stringoperator delete(ptr),   0x835bc0operator delete(ptr),   0x835ba8operator delete(ptr),   0x832398operator delete[](ptr),         0x832380::countNew= 39                                 // 13 + 13 + 13::countArrayNew= 16                            ::timesNew= 4

func3

void func3(){    vector<int> vec(10);    // vector object 自身不是 dynamic allocated          vec.push_back(1);    vec.push_back(1);    vec.push_back(1);        cout << "::countNew= " << ::countNew << endl;    cout << "::timesNew= " << ::timesNew << endl;}

输入:

operator new,   40                             //  10 intsoperator new,   80operator delete(ptr),   0x772380::countNew= 120                                // 80 + 40::timesNew= 2operator delete(ptr),   0x775ba8

func4

void func4(){    list<int> lst;     // list object 自身不是 dynamic allocated      lst.push_back(1);      lst.push_back(1);    lst.push_back(1);        cout << "::countNew= " << ::countNew << endl;    cout << "::timesNew= " << ::timesNew << endl;} 

输入

operator new,   12    // 每个 node 是 12 bytesoperator new,   12operator new,   12::countNew= 36::timesNew= 3operator delete(ptr),   0x712380operator delete(ptr),   0x712398operator delete(ptr),   0x715ba8
重点

func5

void func6(){    list<double> lst;    for (int i=0; i< 1000000; ++i)        lst.push_back(i);    cout << "::countNew= " << ::countNew << endl;    //16000000 (留神, node 都帶 cookie)    cout << "::timesNew= " << ::timesNew << endl;    //1000000}

func6

template <typename T>using listPoll = list<T, __gnu_cxx::__pool_alloc<T>>;void func5(){    //list<double, __gnu_cxx::__pool_alloc<double>> lst;    //上一行改用 C++/11 alias template 來寫 :    listPoll<double> lst;    for (int i=0; i< 1000000; ++i)        lst.push_back(i);    cout << "::countNew= " << ::countNew << endl;      //16752832 (留神, node 都不帶 cookie), 因为增加了额定的内存治理,因而申请了多于 func6 的申请量    cout << "::timesNew= " << ::timesNew << endl;       //122}

G2.9 std::alloc 移植至 C

std::alloc 整个由 static data member 和 static member functions 形成,整体只有一个 class 而无分支,因而移植至 C 很容易。需注意:

chunck_alloc(size_t size, int &nobjs);

pass by reference 移至 C 时或需改为 pass by pointer

移植步骤
  • 除去 template
  • 将 union obj 移至 _default_alloc_template 内部独立
  • 将所有 static data 移为 global
  • 将 __malloc_alloc 改名为 malloc_alloc, 将 __default_alloc 改名为 alloc
  • 将 malloc_alloc 的所有 static function 移为 global
  • 将 alloc 的所有 static functions 移为 global
  • 将 .cpp 改为 .c, 将上述 pass by reference 改为 pass by pointer, 再将:

    union obj {  union obj *free_list_link;};==>typedef union __obj {  union __obj *free_list_link;}obj;或typedef struct __obj {  struct __obj *free_list_link;}obj;

alloc.h

// author : Hou Jie (侯捷)// date : 2015/11/11 // compiler : DevC++ 5.61 (MinGW with GNU 4.9.2)//// 說明:這是侯捷 E-learning video "C++內存治理" 的實例程式.//// filename : allocc.h// 取材自 SGI STL 2.91 <stl_alloc.h>, 移植至 C language.#include <stdlib.h>  //for malloc(),realloc()#include <stddef.h>  //for size_t#include <memory.h>  //for memcpy()//#define __THROW_BAD_ALLOC   cerr << "out of memory" << endl; exit(1)#define __THROW_BAD_ALLOC   exit(1)//----------------------------------------------// 第1級配置器。//----------------------------------------------void (*oom_handler)() = 0;void* oom_malloc(size_t n){  void (*my_malloc_handler)();  void* result;  for (;;) {    //不斷嘗試釋放、配置、再釋放、再配置…    my_malloc_handler = oom_handler;    if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }    (*my_malloc_handler)();    //呼叫處理常式,企圖釋放記憶體    result = malloc(n);        //再次嘗試配置記憶體    if (result) return(result);  }}void* oom_realloc(void *p, size_t n){  void (*my_malloc_handler)();  void* result;  for (;;) {    //不斷嘗試釋放、配置、再釋放、再配置…    my_malloc_handler = oom_handler;    if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }    (*my_malloc_handler)();    //呼叫處理常式,企圖釋放記憶體。    result = realloc(p, n);    //再次嘗試配置記憶體。    if (result) return(result);  }}void* malloc_allocate(size_t n){  void *result = malloc(n);   //间接应用 malloc()  if (0 == result) result = oom_malloc(n);  return result;}void malloc_deallocate(void* p, size_t n){  free(p);  //间接应用 free()}void* malloc_reallocate(void *p, size_t old_sz, size_t new_sz){  void* result = realloc(p, new_sz); //间接应用 realloc()  if (0 == result) result = oom_realloc(p, new_sz);  return result;}void (*set_malloc_handler(void (*f)()))(){ //類似 C++ 的 set_new_handler().  void (*old)() = oom_handler;  oom_handler = f;  return(old);}//----------------------------------------------//第二級配置器//----------------------------------------------enum {__ALIGN = 8};                        //小區塊的上調邊界enum {__MAX_BYTES = 128};                  //小區塊的下限enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free-lists 個數// union obj {                   //G291[o],CB5[x],VC6[x]//   union obj* free_list_link;  //這麼寫在 VC6 和 CB5 中也能够,// };                            //但以後就得应用 "union obj" 而不能只寫 "obj"typedef struct __obj {  struct __obj* free_list_link;} obj;char*   start_free = 0;char*   end_free = 0;size_t  heap_size = 0;obj* free_list[__NFREELISTS]     = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };size_t ROUND_UP(size_t bytes) {    return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));}size_t FREELIST_INDEX(size_t bytes) {    return (((bytes) + __ALIGN-1)/__ALIGN - 1);}//----------------------------------------------// We allocate memory in large chunks in order to// avoid fragmentingthe malloc heap too much.// We assume that size is properly aligned.//// Allocates a chunk for nobjs of size "size".// nobjs may be reduced if it is inconvenient to// allocate the requested number.//----------------------------------------------//char* chunk_alloc(size_t size, int& nobjs)  //G291[o],VC6[x],CB5[x]char* chunk_alloc(size_t size, int* nobjs){  char* result;  size_t total_bytes = size * (*nobjs);   //原 nobjs 改為 (*nobjs)  size_t bytes_left = end_free - start_free;  if (bytes_left >= total_bytes) {      result = start_free;      start_free += total_bytes;      return(result);  } else if (bytes_left >= size) {      *nobjs = bytes_left / size;         //原 nobjs 改為 (*nobjs)      total_bytes = size * (*nobjs);      //原 nobjs 改為 (*nobjs)      result = start_free;      start_free += total_bytes;      return(result);  } else {      size_t bytes_to_get =                 2 * total_bytes + ROUND_UP(heap_size >> 4);      // Try to make use of the left-over piece.      if (bytes_left > 0) {          obj* volatile *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);      if (0 == start_free) {          int i;          obj* volatile *my_free_list, *p;          //Try to make do with what we have. That can't          //hurt. We do not try smaller requests, since that tends          //to result in disaster on multi-process machines.          for (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));                  //Any leftover piece will eventually make it to the                  //right free list.              }          }          end_free = 0;       //In case of exception.          start_free = (char*)malloc_allocate(bytes_to_get);          //This should either throw an exception or          //remedy the situation. Thus we assume it          //succeeded.      }      heap_size += bytes_to_get;      end_free = start_free + bytes_to_get;      return(chunk_alloc(size, nobjs));  }}//----------------------------------------------// Returns an object of size n, and optionally adds// to size n free list. We assume that n is properly aligned.// We hold the allocation lock.//----------------------------------------------void* refill(size_t n){    int nobjs = 20;    char* chunk = chunk_alloc(n,&nobjs);    obj* volatile *my_free_list;   //obj** my_free_list;    obj* result;    obj* current_obj;    obj* next_obj;    int i;    if (1 == nobjs) return(chunk);    my_free_list = free_list + FREELIST_INDEX(n);    //Build free list in chunk    result = (obj*)chunk;    *my_free_list = next_obj = (obj*)(chunk + n);    for (i=1;  ; ++i) {      current_obj = next_obj;      next_obj = (obj*)((char*)next_obj + n);      if (nobjs-1 == i) {          current_obj->free_list_link = 0;          break;      } else {          current_obj->free_list_link = next_obj;      }    }    return(result);}//----------------------------------------------////----------------------------------------------void* allocate(size_t n)  //n must be > 0{  obj* volatile *my_free_list;    //obj** my_free_list;  obj* result;  if (n > (size_t)__MAX_BYTES) {      return(malloc_allocate(n));  }  my_free_list = free_list + FREELIST_INDEX(n);  result = *my_free_list;  if (result == 0) {      void* r = refill(ROUND_UP(n));      return r;  }  *my_free_list = result->free_list_link;  return (result);}//----------------------------------------------////----------------------------------------------void deallocate(void *p, size_t n)  //p may not be 0{  obj* q = (obj*)p;  obj* volatile *my_free_list;   //obj** my_free_list;  if (n > (size_t) __MAX_BYTES) {      malloc_deallocate(p, n);      return;  }  my_free_list = free_list + FREELIST_INDEX(n);  q->free_list_link = *my_free_list;  *my_free_list = q;}//----------------------------------------------////----------------------------------------------void* reallocate(void *p, size_t old_sz, size_t new_sz){  void * result;  size_t copy_sz;  if (old_sz > (size_t) __MAX_BYTES && new_sz > (size_t) __MAX_BYTES) {      return(realloc(p, new_sz));  }  if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);  result = allocate(new_sz);  copy_sz = new_sz > old_sz? old_sz : new_sz;  memcpy(result, p, copy_sz);  deallocate(p, old_sz);  return(result);}//----------------------------------------------