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分配内存则不予回收,程序员仍是须要本身控制堆内存的回收。