C++堆內存管理 python
在很早的C++98以前,C++用"auto_ptr"智能指針來管理堆分配的內存,它的使用很是簡單: 程序員
auto_ptr<int> ap(new int(1024)); 算法
即將new操做返回的指針做爲auto_ptr的初始值,不用調用delete便可實現堆內存的自動釋放(如析構的時候)。 數組
因爲auto_ptr自己存在一些問題,它在C++11中被拋棄了。例如 緩存
1. auto_ptr不能共享指向對象的全部權,由於auto_ptr不含有賦值語義,而是轉移語義,即對象控制權的轉移。
2. auto_ptr不能指向數組。由於其實現中調用的是delete而非delete[]。
3. auto_ptr不能做爲容器類的元素,由於不知足容器的要求,複製或賦值後,兩個對象必須具備相同值。 安全
取而代之的是unique_ptr、share_ptr、weak_ptr等智能指針來回收有堆分配的對象。 函數
unique_ptr顧名思義即沒法複製的智能指針,以下: spa
unique_ptr<int> var_ptr1(new int(11)); 設計
它不能與其餘的unique_ptr指針對象共享所指向的內存,以下的表達式是不容許的: 指針
unique_ptr<int> var_ptr2= var_ptr1;
可是能夠經過:
unique_ptr<int> var_ptr2=move(var_ptr1);
將var_ptr1的全部權轉移給var_ptr2。
這裏unique_ptr和auto_ptr同樣不能共享指向對象的全部權。簡單的狀況下使用unique_ptrk能夠直接代替auto_ptr指針。爲了解決auto_ptr不能共享對象內存是全部權的這一問題,C++11引入了share_ptr。
share_ptr容許多個智能指針共享同一對象由堆所分配的內存,
share_ptr<int> var_ptr3(new int(12));
share_ptr<int> var_ptr4= var_ptr3;
在var_ptr3將內存釋放後,
var_ptr3.reset();
即顯式的調用var_ptr3.reset()後,var_ptr4所指向的爲原來var_ptr3所分配的內存不受任何影響,而只是將指向這塊內存的引用計數減一,若是引用計數減到0後,說明這塊內存的全部者都不須要這塊內存了,share_ptr才真正釋放堆內存空間。
可是如何知道一個share_ptr智能指針的引用計數減爲0了,也就是說如何判斷share_ptr的有效性呢?weak_ptr智能智能的lock成員能夠幫上忙。
weak_ptr操做也很簡單,以下:
share_ptr<int> var_ptr3(new int(12));
share_ptr<int> var_ptr4= var_ptr3;
weak_ptr<int> w_ptr= var_ptr3;
share_ptr<int> ptr = w_ptr.lock();
經過ptr是否爲空便可判斷share_ptr的有效性。
總而言之,unique_ptr在通常的狀況下能夠代替auto_ptr指針,而share_ptr和weak_ptr則能夠用在須要引用計數的地方。
智能指針能夠有效的幫助程序員管理堆內存,可是須要顯式的聲明智能指針,可是向其餘的一些語言如JAVA和python則徹底不須要考慮回收指針類型,由於他們支持垃圾回收機制,而C++目前只支持最小垃圾回收機制。
垃圾回收的方法:
基於引用計數
引用計數的方法比較簡單,在系統分配堆內存給一個對象後引用計數加一,當某一個對象釋放堆內存後引用計數減一,直到引用計數爲0,被分配給對象的內存則被回收。
優勢:不會形成程序暫停,不會對系統緩存和交換空間形成衝擊。
缺點:不能解決"環形引用"的問題,計數開銷不小。
基於跟蹤處理
基於跟蹤處理的垃圾回收機制的基本思想是產生跟蹤對象的關係圖。
從根對象開始查找它們所引用的堆空間,並在這些堆空間上作標記,當標記結束後,全部的被標記的對象爲可達對象或活對象,沒有被標記的則被認爲是垃圾,而後這些垃圾被回收。
缺點:活對象因爲不會被移動則會產生大量的內存碎片。
此方法和和標記清除的方法同樣,可是在標記完以後會將可達對象也就是活對象向左靠齊,由此解決了內存水平地問題。
此算法實際上是標記整理的另一種實現方式,它也有一些問題就是對的利用率只有一半,也須要移動活對象。
C++目前只支持最小垃圾回收機制,這其中最主要的緣由是C/C++對指針操做的靈活性,固然這也是C/C++的特色和優點,由於這是的程序員能夠直接操做內存,這也是爲何C++程序更加高效的緣由之一,可是正是因爲這個特色和優點使得C/C++要實現內存垃圾回收會存現一些"不安全的"情況,這致使了C++到目前爲止尚未徹底支持垃圾回收。
爲何說C++對指針的操做會致使垃圾回收時產生不安全的因素呢?看下式:
Int *p =new int;
P+=10;
p-=10;
*p=10;
在上面的操做中咱們能夠看出,在指針移動後,若是垃圾回收器被設計爲這個時候回收p原來指向的內存,則會致使p再次移動回原來位置的時候指向了一個無效的地址(指針已經被回收了);後面的*p=10對這個無效的指針進行操做可想而知後果是什麼,這就致使設計垃圾回收器的時候進入了一個兩難的境地,如何設計才能保證內存垃圾被正確的回收,這就給C++的垃圾回收器的設計帶來挑戰,究竟是從編譯器端着手解決這些問題,仍是其餘方式,如今尚未定論。
正是因爲這些安全的問題,致使C++目前爲止只支持最小垃圾回收機制。最下垃圾回收機制針對提出了安全派生指針,它是指由new分配的對象或其子對象的指針。
【查看編譯器是否支持這個特性】,能夠經過下式:
Point_safety get_pointer_safty() noexcept
若是它返回point_safety類型的值,若是值爲pointer_safety::strict, 則代表編譯器支持最小垃圾回收及安全派生指針,若是返回爲pointer_safety::relax或pointer_safety::preferred則代表編譯器不支持。
【通知垃圾回收器不得回收某資源】可經過下面的接口實現。
Void declare_reachable(void * p);
即通知垃圾回收器某一資源爲可到達,這樣垃圾回收器就不會回收該資源。
Template <class T> T *undeclare_reachable(T *p) noexcept;
將資源的可達聲明取消,垃圾回收器可見該資源,則能夠回收該資源。
【對大片連續內存的操做】有如下API實現:
Void declare_no_pointers(char *p,size_t n) noexcept;
這個函數能夠告訴垃圾回收器*p指向的n大小的內存不存在有效的指針。
Void undeclare_no_pointers(char *p,size_t n) noexcept;
這個函數能夠告訴垃圾回收器*p指向的n大小的內存存在有效的指針。
C++11標準中針對垃圾回收的支持僅限於new操做符分配的內存,而用malloc分配內存則不予回收,程序員仍是須要本身控制堆內存的回收。