C++堆內存管理

C++堆內存管理 python

 

  1. auto_ptr的缺陷

在很早的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等智能指針來回收有堆分配的對象。 函數

  1. unique_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。

 

  1. 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成員能夠幫上忙。

 

  1. share_ptr指針

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則能夠用在須要引用計數的地方。

  1. 垃圾回收機制

智能指針能夠有效的幫助程序員管理堆內存,可是須要顯式的聲明智能指針,可是向其餘的一些語言如JAVA和python則徹底不須要考慮回收指針類型,由於他們支持垃圾回收機制,而C++目前只支持最小垃圾回收機制。

垃圾回收的方法:

基於引用計數

引用計數的方法比較簡單,在系統分配堆內存給一個對象後引用計數加一,當某一個對象釋放堆內存後引用計數減一,直到引用計數爲0,被分配給對象的內存則被回收。

優勢:不會形成程序暫停,不會對系統緩存和交換空間形成衝擊。

缺點:不能解決"環形引用"的問題,計數開銷不小。

 

基於跟蹤處理

基於跟蹤處理的垃圾回收機制的基本思想是產生跟蹤對象的關係圖。

  1. 標記-清除

    從根對象開始查找它們所引用的堆空間,並在這些堆空間上作標記,當標記結束後,全部的被標記的對象爲可達對象或活對象,沒有被標記的則被認爲是垃圾,而後這些垃圾被回收。

    缺點:活對象因爲不會被移動則會產生大量的內存碎片。

  2. 標記-整理

    此方法和和標記清除的方法同樣,可是在標記完以後會將可達對象也就是活對象向左靠齊,由此解決了內存水平地問題。

  3. 標記-拷貝

    此算法實際上是標記整理的另一種實現方式,它也有一些問題就是對的利用率只有一半,也須要移動活對象。

    1. C++最下垃圾回收機制

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分配的對象或其子對象的指針。

  1. 在解引用基礎上的引用,好比:&*p。
  2. 定義明確的指針操做,好比:p+1;
  3. 定義明確的指針轉換,好比:static_cast<void>(p).
  4. 指針和整型之間的reinterpret_cast,好比:reinterpret_cast<intptr_t>(p)

     

    【查看編譯器是否支持這個特性】,能夠經過下式:

    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大小的內存存在有效的指針。

     

    1. C++最小垃圾回收機制的支持

C++11標準中針對垃圾回收的支持僅限於new操做符分配的內存,而用malloc分配內存則不予回收,程序員仍是須要本身控制堆內存的回收。

相關文章
相關標籤/搜索