C++的内存管理机制

优点:

C++使用new在运行时,从堆里给程序分配一块内存空间,开发者可以直接拿到内存的地址进行操作,直接访问内存地址的方式可以提高内存使用的灵活性,当这块内存不再使用的时候使用delete将内存释放。

缺点:

极易出错,比如出现:

野指针:指针指向的内存已经被释放,而其他指针可能仍然指向这块内存空间,然而这块内存空间可能已经被分配了其他的内容,贸然使用可能会出现不可预知的结果。

重复释放:如果两个指针同时指向一块内存,然而对这两个指针都进行内存释放操作;或者针对一个指针释放两次内存,都会导致C++运行时错误。

内存泄漏:如果new了一块内存空间,但是忘记释放了,那么这块内存空间就会一直被占用。游戏中内存泄漏的问题由为严重,因为游戏需要时刻创建和删除元素,处理着大量的内存操作,也非常容易有很大一块内存忘记释放。而所有设备的内存空间都是有限的,占着大量的内存,可能会导致程序退出等后果。

C++11中的智能指针

优点

C++中根据内存分配方式可以分为三种变量:static静态变量和常量(存储在数据区);动态变量(存储在堆区);局部变量(存储在栈区)。

上一节我们讲了动态变量的优势和劣势。这里说一下局部变量:局部变量存储在栈中,当局部变量被创建的时候,进行压栈操作,作用域为从定义到函数结束,当函数结束的时候,按照顺序进行出栈操作。优势在于开发者不需要对局部变量的生命周期进行控制,而这个优势正是动态变量所需要的。

智能指针就是在创建一个动态变量的时候,将其与一个局部变量进行绑定,这样即具备动态变量的优势(可以提高内存使用的灵活性),又具有局部变量的优势(开发者不需要对其生命周期进行控制)。当局部变量离开作用域被自动释放的时候,同时释放动态变量所对应的内存空间。

分类

unique_ptr指针:
定义方式:unique_ptr up1(new int(5))

不能与其他智能指针共享内存,如果运行 unique_ptr up11 = up1,编译时会报错。可以通过move函数转移内存的所有权,一旦转移成功,原先的unique_ptr就失去了对内存的所有权,再使用的话会报错。

通过*up1访问内存块,退出函数或者执行变量的reset函数的时候,会释放内存。

shared_ptr指针:
定义方式:shared_ptr up2(new int(5))

可以与其他智能指针共享内存,采用引用基数的方式管理内存,只有当所有智能指针都执行reset函数,或者离开作用域的时候,才会真正的释放内存。

weak_ptr指针:
定义方式:weak_ptr up3 = up2

可以指向shared_ptr的内存,但是不拥有该内存。通过其lock成员访问该内存,当该内存无效的时候,返回nullptr,常用于验证shared_ptr指针的有效性。

缺点

shared_ptr为了保证线程安全,加入了互斥锁。互斥锁对性能会有影响。

创建shared_ptr需要显示声明智能指针,使用指针的时候最好使用weak_ptr来间接访问内存。编写代码的时候注意事项较多。

垃圾回收机制

原理

使用引用计数记录对象被引用的次数,当次数为0时,该对象被视为垃圾被回收。

将程序中正在使用的对象所引用的内存空间做标记,未做标记的内存进行整理回收。

C++不支持垃圾回收机制

Cocos2d-x的内存管理机制

使用引用计数

不管是shared_ptr还是垃圾回收机制,都借用了引用计数这个原理。学过Objective-C编程的同学更是会对retain和release这2个函数耳熟能详,这两个函数的作用就是修改引用计数。retain是将引用计数加1,release是将引用计数减1。

Cococs2d-x的引擎内部就是使用了引用计数来进行内存管理。在基类Ref中,定义着retain和release这2个函数,对引用计数进行操作。在元素被new创建的时候,引用计数为1,执行一次retain,引用计数为2,执行一次release,引用计数为1,当引用计数为0的时候,执行delete,将元素删除。

使用”智能指针”

对元素运行autorelease函数,将其加入AutoReleasePool。AutoReleasePool在每一帧结束的时候,就会对Pool里面所有的元素执行一次release函数。

比如一个元素在创建的时候,引用计数为1,加入AutoReleasePool,当AutoReleasePool在一帧结束的时候,自动执行release操作,引用计数为0,delete。

为了简化代码,Cocos2d-X将new和autorelease函数包装成了一个函数,create。元素直接执行create函数,就完成了创建并加入autoreleasepool的操作。

自定义AutoReleasePool

用户可以根据自己的需要自己创建AutoReleasePool,AutoReleasePool是由PoolManager管理的,按照栈的方式存放,默认有一个AutoReleasePool,用户可以创建一个属于自己的AutoReleasePool。

创建AutoReleasePool是用于存放Object的,所以理论上需要一块内存,需要内存就需要调用new方法从堆区获取内存,但是这样的话,用户又要自己控制AutoReleasePool的内存管理,不符合Cocos2d-X的宗旨。所以Cocos2d-X在AutoReleasePool的构造和析构函数中加入了分配内存和压栈和出栈的操作,开发者只需要创建一个局部变量的AutoReleasePool即可即享受内存,又自动控制生命周期。

创建了一个属于开发者自己的AutoReleasePool之后,经过了压栈操作,当前的AutoReleasePool就是开发者创建的这个AutoReleasePool了,此后执行autorelease函数的元素都将加入用户创建的这个AutoReleasePool,在AutoReleasePool作用域结束的时候,调用AutoReleasePool的析构函数,将AutoReleasePool中的元素release,将AutoReleasePool出栈。