Cocos2d-x 3.x memory management mechanism 1:c++ memory management 1-1: memory allocation area
Creating an object requires two steps: The first step is to allocate memory for the object, and the second step is to call the constructor to initialize the memory. In the first step, you can select several different allocation areas. These areas are as follows:
(1) Stack area allocation. The stack memory allocation operation is built into the processor's instruction set, which is very efficient, but allocates a limited amount of memory. It is automatically allocated and freed by the processor to hold the parameter values of the function and the values of the local variables. When executing a function, the storage units of local variables within the function can be created on the stack, which are automatically freed when the function is executed at the end.
(2) Heap area allocation. Allocated from the heap, also known as dynamic memory allocation. Released by the developer, if not released, is reclaimed by the operating system at the end of the program. When a program uses malloc or new to request any amount of memory at runtime, it is the developer's responsibility to release memory with free or delete. The lifetime of dynamic memory is determined by the developer and is very flexible to use, but the problem is the most.
(3) Allocation in static storage area. This memory space exists during the entire run of the program and is already allocated when the program is compiled. It can assign global variables and static variables.
1-2: Dynamic Memory allocation
Dynamic memory allocation is the most flexible but there are a lot of problems here, which focuses on dynamic memory allocation. Dynamic memory allocates memory using malloc or new, freeing memory with free or delete. where malloc and free are paired, new and delete are paired.
(1) Use of malloc and free
malloc and free are standard library functions in the C/C + + language, mainly used in C. Creating an object using malloc does not automatically invoke the constructor to initialize memory. Releasing an object with free does not automatically call the destructor to clear memory.
(2) Use of new and delete
Unlike malloc and free, new and delete are not function libraries, but C + + operators. The new operator is able to complete all steps of creating the object (that is, the first step, allocating memory for the object, and the second, calling the constructor to initialize the memory), which also invokes the constructor. The instance code is as follows:
MyObject * obj = new MyObject();
The constructor can be overloaded, depending on the parameter list passed by the user, deciding which constructor to invoke to initialize the object.
The inverse operator of the new operator is that Delete,delete first calls the destructor and then frees the memory. The instance code is as follows:
delete obj;
Where obj is an object pointer, obj can only release objects created by new and cannot be freed by malloc. And with delete-freed object pointers, obj = null is required to prevent "wild pointers".
Tip: One case is that the pointer variable is not initialized, its point is random, it is not null. If the IF statement is used, it is considered a valid pointer. Another case is that after the pointer variable is free or delete, they simply release the memory that the pointer refers to, but do not clear the pointer itself, and the pointer is pointing to "junk" memory. It is also considered a valid pointer if it is judged by the IF statement. The "Wild pointer" is dangerous, and good programming practice is that you need to set the pointer to null in both cases. This is the only way to avoid "wild pointers".
2:cocos2d-x Memory Management
In the 3.x version, Cocos2d-x uses a new root class ref, which implements a reference count record for Cocos2d-x class objects. All classes in the engine are derived from ref. Cocos2d-x memory Management is built on the C + + language New/delete and is implemented by introducing a reference count for the Object-c language.
2-1: Memory Reference count
The ref class is designed from the Ccobject class of Cocos2d-iphone and is also called the Ccobject class in Cocos2d-x 2.x. Therefore, the memory management of the ref class is designed to refer to objectives Manual management of reference counts (reference COUNT,RC).
Each ref object has an internal counter that tracks the number of references to the object, called the reference count (RC). When the object is created, the reference count is 1. In order to guarantee the existence of an object, the retain function can be called to hold the object, retain will make its reference count plus 1, and if this object is not required to call the release function, release makes its reference count minus 1. When the object's reference count is 0 o'clock, the engine knows that the object is no longer needed, and the object memory is freed by the delete.
The Core class Ref: implements a reference count. /** * CCRef.h **/class cc_dll ref{public:void retain (); Keep. Reference count +1 void release (); Release. Reference count-1 ref* autorelease (); For automatic release. unsigned int getreferencecount () const; Number of citations protected:ref (); Initialize Public:virtual ~ref (); destructor protected:unsigned int _referencecount; Number of citations friend class Autoreleasepool; Auto Free Pool};/** * CCRef.cpp **///node is created, the number of references is 1ref::ref (): _referencecount (1) {}void Ref::retain () {Ccassert (_referencec Ount > 0, "Reference count should greater than 0"); ++_referencecount;} void Ref::release () {ccassert (_referencecount > 0, "Reference count should greater than 0"); --_referencecount; if (_referencecount = = 0) {Delete this; }}
Cocos2d-x provides reference counting to manage memory in the following ways:
- Call the Retain () method: make its reference count increase by 1, indicating that the object's reference is obtained.
- Call the Release () method: At the end of the reference, make its reference count value minus 1, which means that the object's reference is disposed.
- Call the Autorelease () method: Put the object into the auto-free pool. When the release pool itself is freed, it executes the release () method once for all objects in the pool, enabling a flexible garbage collection. Cocos2d-x provides Autoreleasepool, which manages the automatic release of objects. When the release pool itself is freed, it executes the release () method once for all objects in the pool.
Ref principle Analysis:
- When a ref is initialized (when new comes out), _referencecount = 1;
- When the Retain () method of the ref is called, _referencecount++;
- When the release () method of the ref is called, _referencecount–;
- If the _referencecount minus is 0, delete the ref.
2-2:autorelease use
When an Obj object that inherits from ref is created, its reference count _referencecount to 1, and once Autorelease () is executed, the Obj object is added to the current auto-release pool Autoreleasepool. It manages the pool of objects that are about to be disposed. The reference count value of the Obj object does not decrease by 1. However, at the end of a frame or at the end of a message loop , the current auto-release pool is reclaimed and a release () operation is performed on all objects in the auto-release pool, and when the object's reference count is 0 o'clock, the object is freed.
So-called a frame or a message loop, which is a gameloop (in the Director class). each time in order to handle the new event, the Cocos2d-x engine creates a new auto-free pool, and when the event is processed, the pool is destroyed, the reference count of the objects in the pool is reduced by 1, and if the reference count is reduced by 0, that is, the object is not retain by another class or ref object, Otherwise, this object will not be released and "survived" during the destruction of the pool, which is transferred to the next pool to survive. To sum up is:
The system destroys the current auto-release pool at the end of each frame and creates a new auto-release pool;
The auto-free pool performs a release () operation on all objects in the pool when it is destroyed;
The newly created object is automatically freed if it is not used within a frame;
2-2-1 autorelease Source code is as follows:
Ref* Ref::autorelease(){ // 将节点加入自动释放池 PoolManager::getInstance()->getCurrentPool()->addObject(this); return this;}
2-2-2 classes related to Autorelease are as follows:
(1) Autoreleasepool class: Manages a vector array to hold objects that are added to the auto-release pool. Provides an empty operation on the release pool.
// 存放释放池对象的数组std::vector<Ref*> _managedObjectArray;// 往释放池添加对象void AutoreleasePool::addObject(Ref* object){ _managedObjectArray.push_back(object);}// 清空释放池,将其中的所有对象都 deletevoid AutoreleasePool::clear(){ // 释放所有对象 for (const auto &obj : _managedObjectArray) { obj->release(); } // 清空vector数组 _managedObjectArray.clear();}// 查看某个对象是否在释放池中bool AutoreleasePool::contains(Ref* object) const{ for (const auto& obj : _managedObjectArray) { if (obj == object) return true; } return false;}
(2) Poolmanager class: Manages a vector array to hold the auto-free pool. By default the engine creates only one auto-free pool, so this class is available to developers, such as adding their own auto-free pools for performance reasons.
//Release pool manager Singleton object static poolmanager* s_singleinstance;//Free Pool Array std::vector<autoreleasepool*> _ releasepoolstack;//get release pool manager Singleton poolmanager* Poolmanager::getinstance () {if (s_singleinstance = = nullptr) {/ /Create a new manager Object s_singleinstance = new Poolmanager (); Add an auto-free pool new Autoreleasepool ("Cocos2d autorelease Pool");//internal use of the release pool manager push, where the call is very subtle, the reader can take a look at the} return S_singleinstance;} Gets the current release pool autoreleasepool* Poolmanager::getcurrentpool () const{return _releasepoolstack.back (); See if the object is in a free pool bool Poolmanager::isobjectinpools (ref* obj) const{for (const auto& Pool: _releasepoolstack) { if (Pool->contains (obj)) return true; } return false;} Add release Pool object void Poolmanager::p ush (Autoreleasepool *pool) {_releasepoolstack.push_back (pool);} Release pool object out of stack void Poolmanager::p op () {Cc_assert (!_releasepoolstack.empty ()); _releasepoolstack.pop_back ();}
We can create autoreleasepool ourselves and manage the autorelease of objects.
We already know that the object that called the Autorelease () method (hereinafter referred to as the "Autorelease object") will be freed once the automatic release of the pool is released. Although Cocos2d-x has ensured that the release pool is released once each frame ends and a new release pool is created before the next frame starts, we should also consider that the release pool itself maintains a list of objects that will perform the release operation, and if a large number of Autorelease objects are generated within a frame, Will cause the performance of the release pool to degrade. Therefore, before and after generating Autorelease object-dense areas (usually loops), it is better to manually create and release a recycling pool.
(3) Displaylinkdirector class: This is a Director class that provides the main loop of the game to achieve the release of resources for each frame. The name of this class looks a bit strange, but it's not a control. Because this class inherits the Director class and is the only class that inherits the director, which means it can be combined into a class, the engine developer has some explanation in the source code.
void DisplayLinkDirector::mainLoop(){ //第一次当导演 if (_purgeDirectorInNextLoop) { _purgeDirectorInNextLoop = false; purgeDirector();//进行清理工作 } else if (! _invalid) { // 绘制场景,游戏主要工作都在这里完成 drawScene(); // 清空资源池 PoolManager::getInstance()->getCurrentPool()->clear(); }}
2-2-3 Summary:
The essence of Autorelease () is that the object is added to the auto-release pool, and the object's reference count is not immediately reduced by 1, and the object executes release () when the auto-free pool is reclaimed.
Autorelease () A release occurs only if the auto-free pool is freed, and if the object is freed more than expected, the error is not discovered when calling Autorelease (). The game crashes only when the auto-free pool is released (usually at the end of every frame of the game). In this case, locating the error becomes very difficult. For example, in a game, an object contains 1 reference counts, but it is called two times autorelease (). In the second call to Autorelease (), the game will continue to execute this frame, the end of the game will only crash, it is difficult to find the wrong place in time. Therefore, we recommend that the misuse of autorelease () should be avoided during the development process and used only when the factory method has to be used, releasing the object reference as much as release ().
Autorelease () is not cost-free, and the release pool mechanism behind it also needs to occupy memory and CPU resources. Excessive use of autorelease () increases the expense of automatic release pool management and freeing pool maintenance object access. In the memory and CPU resources are not enough programs to make system resources more tense. At this point, we need to reasonably create an automatic release Pool management Object autorelease.
Unused objects recommend using release () to dispose of object references and reclaim them immediately.
3:REF Special Memory Management
(1) node's Addchild/removechild method
In Cocos2d-x, all inherit from the node class, and the child nodes automatically call retain when the Addchild method is called to add child nodes. When a child node is removed through removechild, the child node automatically calls release.
Call the Addchild method to add a child node, and the node object executes retain. Child nodes are added to the node container, and when the parent node is destroyed, the node container is destroyed to release the child nodes, that is, the child node executes release. If you want to remove the child nodes early, we can call removechild.
In Cocos2d-x memory management, in most cases we automatically complete the retain,release call by calling Addchild/removechild. You do not need to call Retain,release again.
(2) tree-shaped structure and chain reaction
We currently run this scene, the scene is initialized, add a lot of layers, the layer has other layers or sprites, and these are ccnode nodes, take the scene as root, form a tree structure, after the scene is initialized (after a frame), these nodes will be completely Attachment (internal through retain) on this tree structure, the full authority of the tree to manage, when we cut a branch, or the tree uprooted, then the "sub-node" above it will also be removed (internal through release), this is the chain reaction.
(3) Factory method
In Cocos2d-x, a number of factory methods are provided to create a static function object. Looking closely you will find that these objects are automatically released.
#define CREATE_FUNC(__TYPE__) static __TYPE__* create() { __TYPE__ *pRet = new __TYPE__(); if (pRet && pRet->init()) { pRet->autorelease(); return pRet; } else { delete pRet; pRet = NULL; return NULL; } }
The following section of code illustrates the Cocos2d-x object auto-release process.
void HelloWorld::test(){ auto lable = Lable::create(); this->addChild(lable);}
- First, a lable is created by the Create Method (Lable->autorelease () is executed during creation), and the object is added to the current auto-release pool Autoreleasepool, at which point the reference count of the label is 1;
- Execution This->addchild (lable) joins the label as a child node in the Layer parent node, at which point the reference count for the label is 2;
- When a frame is finished, the current auto-release pool Autoreleasepool is released, a release operation is performed on the label, the reference count of the label becomes 1, the current auto-release pool is destroyed, and the function of the lable->autorelease () ends;
- When the game is over or switching scenes, the label's parent layer is destroyed, a chain reaction occurs, the layer performs a release on its child node label, the label's reference count becomes 0, the delete label operation is performed, and the memory is reclaimed.
4: Memory Management usage recommendations
(1) The memory allocated by new is used inside the class, and the delete operation is performed in the destructor;
(2) The Cocos2d-x class element is created by creating a static factory, and its memory is automatically managed through autorelease without additional action;
(3) Each retain function must correspond to a release function or a autorelease function;
(4) Singleton mode because there is only one instance, you can write a member function for the Delete object pointer, at the end of the game to execute the member function on it;
Cocos2d-x Learning notes-memory management mechanism