目錄html
#include <bits/stdc++.h> using namespace std; int main() { /** * c++中內存得到/釋放的幾種方式 * malloc() free() * new delete * ::operator new() ::operator delete() * allocator<T>::allocate() allocator<T>::deallocate() 固然你也能夠調用操做系統API得到內存,可是這樣程序就不具有可移植性 * */ // 初步使用 // [1] malloc free void *p1 = malloc(512); // 512byte free(p1); // [2] complex<int>* p2 = new complex<int>; delete p2; // [3] 這是一種特殊的指針,底層也是調用free和malloc void* p3 = ::operator new(512); //512 byte ::operator delete(p3); // [4] 分配器 STL標準庫的內容,在各個環境下可能不一樣, 我這裏是clang int *p4 = allocator<int>().allocate(5); //VC下有第二個參數(int*)0 allocator<int>().deallocate(p4, 5); //gnu下是 alloc::allocate(512) (這裏是字節了) 上面是幾個int 和 alloc::deallocator //alloc 這個東西是老的版本,在新版還了個名字 //有點麻煩,還要告知還多少內存。 //=========================================== return 0; }
證實new底層是malloc,大概是這個畫風,在VC6的標準庫裏面暢遊。。。c++
void * operator new( unsigned int cb ) { void *res = _nh_malloc( cb, 1 ); return res; }
void * __cdecl _nh_malloc_base (size_t size, int nhFlag) { void * pvReturn; // validate size if (size > _HEAP_MAXREQ) return NULL; #ifndef WINHEAP /* round requested size */ size = _ROUND2(size, _GRANULARITY); #endif /* WINHEAP */ for (;;) { // allocate memory block if (size <= _HEAP_MAXREQ) pvReturn = _heap_alloc_base(size); else pvReturn = NULL; // if successful allocation, return pointer to memory // if new handling turned off altogether, return NULL if (pvReturn || nhFlag == 0) return pvReturn; // call installed new handler if (!_callnewh(size)) return NULL; // new handler was successful -- try to allocate again } }
上面介紹了幾種c++得到內存的方式的使用方式api
當咱們須要內存的時候,能夠用mmap等系統調用直接向操做系統索取內存。可是這樣就不具有可移植性。cookie
因而就出現了malloc函數,由這個函數去實現底層內存的索取,咱們只管要便可。函數
在c++面向對象出來後,咱們若是須要用malloc出對象,須要手動調用構造函數。是比較麻煩的,可是malloc是函數,不在編譯器的控制範圍內,因而就有了new。new的做用,調用malloc,把malloc的指針作類型轉換,而後調用構造函數,返回this
在c++標準庫中,咱們使用vector等容器歷來不關心內存,是由於內部幫咱們實現好了,這就是分配器。spa
例如:操作系統
vector的頭部指針
template<typename _Tp, typename _Alloc = std::allocator<_Tp> > class vector : protected _Vector_base<_Tp, _Alloc>
默認使用std::allocator的構造器調試
template<typename _Tp> class allocator: public __allocator_base<_Tp>
而後這個構造器又繼承另外一個構造器
using __allocator_base = __gnu_cxx::new_allocator<_Tp>;
去找這個構造器,發現它是一個別名。最後咱們找到了new_allocator這個類,下面是這兩個類的兩個函數。
// NB: __n is permitted to be 0. The C++ standard says nothing // about what the return value is when __n == 0. pointer allocate(size_type __n, const void* = static_cast<const void*>(0)) { if (__n > this->max_size()) std::__throw_bad_alloc(); #if __cpp_aligned_new if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { std::align_val_t __al = std::align_val_t(alignof(_Tp)); return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp), __al)); } #endif return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); }
// __p is not permitted to be a null pointer. void deallocate(pointer __p, size_type) { #if __cpp_aligned_new if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { ::operator delete(__p, std::align_val_t(alignof(_Tp))); return; } #endif ::operator delete(__p); }
它是使用::operator new和::operator delete來申請和釋放內存的。
咱們去尋找這兩個東西
在/usr/include/c++/7.3.0
https://en.cppreference.com/w/cpp/header/new
。。。沒有找到我想要的侯捷老師演示的那份原代碼。可是operator new的底層會用malloc嘗試獲取內存,如故沒有得到會容許有一個處理http://www.cplusplus.com/reference/new/
這是標準庫的調用內存過程,萬物迴歸到malloc。
調試過程當中的一些記錄
當你malloc一塊空間的時候,
int a = 1; int b = 1; printf("%x %x\n", &a, &b); //41db2e4c 41db2e48 相差四字節 int *c = new int[1]; int *d = new int[1]; printf("%x %x\n", c, d); //9d269280 9d2692a0 void *e = malloc(sizeof(int)); void *f = malloc(sizeof(int)); printf("%x %x\n", e, f); //aecbd2c0 aecbd2e0
發現這中間的內存間隔是碎片???
Demo*p = new Demo[3]; delete[] p; //delete p int *q = new int[3]; delete q; // or delete[] q
若是沒有指針的釋放操做,new[],搭配delete[],沒寫【】也不會出錯,可是這是很差的寫法。
//placement new 【 new() 】 char *buf = new char[sizeof(Complex) * 3]; Complex *pc = new(buf)Complex(1, 2); //他沒有分配內存,在原來的內存改 /** //上面等價下面部分 void *mem = operator new(sizeof(Complex), buf); pc = static_cast<Complex*>(mem); pc->COmplex::Complex(1, 2); //固然這樣調用構造函數不必定總可行 * */ delete [] buf;
另外一個問題:
咱們malloc出的東西,free操做系統怎麼知道多大呢?
其實,咱們要一塊空間,這塊空間的頭尾都會帶一些東西。咱們能夠稱之爲cookie記錄着這塊空間的信息。而後打包給咱們。
那麼問題來了,咱們每次都要一小塊空間,空間大小都同樣,那麼咱們爲何要那麼多的cookie呢。
因而就引出了內存池的概念。
咱們一次性分配出一大塊空間,重載new和delete,維護一個鏈表來存儲空間,new從鏈表中取,delete在吧空間抓回鏈表。這就避免了大量的cookie。gnu下就有相似的代碼。找了半天才找到。。。
#include <bits/stdc++.h> #include <c++/ext/pool_allocator.h> using namespace std; int main() { //https://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a00015.html //http://gcc.gnu.org/onlinedocs/ vector<int, __gnu_cxx::__pool_alloc<int> >vec; return 0; }