Memory Management
Memory Management has always been an issue that is hard to handle. Developers must consider how and when to allocate and Recycle resources, and perform different optimizations for the heap and stack. The core of memory management is that the dynamically allocated objects must be effectively released after use, that is, the lifecycle of management objects. Since C ++ is a relatively underlying language, its design does not contain any mechanism for Intelligent Memory Management. An object must be recycled after use. However, in a complex program, object ownership is transferred or shared among different program fragments, which makes it very difficult to determine the recovery time, therefore, memory management becomes a headache for programmers.
On the other hand, the collection of scattered objects may cause memory fragmentation in the heap and reduce the memory usage efficiency. Therefore, we need an appropriate mechanism to alleviate this problem.
The smart pointers introduced by the boost Library (and shared pointers introduced by C ++ 11) solve the memory management problem from the perspective of object ownership transfer. However, in many cases, smart pointers still seem thin and powerless, because the relationship between objects in actual development is very complex, and the ownership transfer operation will become redundant during the development process. Therefore, various C ++-based third-party tool libraries and engines often implement their own smart memory management mechanisms to solve the difficulties of memory management, it tries to free developers from tedious and obscure memory management.
Mainstream Memory Management Technologies
Currently, there are two main technologies for implementing Intelligent Memory Management: reference counting and garbage collection.
1) reference count is a very effective mechanism. Each object is maintained with a reference counter to record the number of times the object is currently referenced. When an object is added with a reference, the counter is added with 1. When an object loses a reference, the counter minus 1. When the reference count is 0, the lifecycle of the object ends, this API automatically triggers the release of an object. The important rule of reference count is that each program segment must maintain the reference count responsibly. The reference count is increased and decreased at the beginning and end of the program segment where the object needs to survive, in this way, we can implement flexible and intelligent memory management. In fact, STD: shared_ptr in C ++ is implemented by reference counting.
2) garbage collection it introduces an automatic memory recycler to try to free programmers from complicated memory management tasks. It automatically tracks all references of each object to locate all objects in use, and then releases other objects that are no longer needed. The garbage collector can also compress memory in use to reduce the workspace required by the heap. Garbage collection can prevent memory leakage and effectively use available memory. However, the garbage collector is usually run as a separate low-level thread, in unpredictable situations, the programmer cannot manually assign a garbage collector to clear and recycle dead or unused objects in the memory heap. The collection mechanism includes generational replication garbage collection, marking garbage collection, and incremental garbage collection. Generally, the garbage collection mechanism is applied to hosted languages (that is, running in virtual machines ), such as C #, Java, and some scripting languages.
Cocos2d-x Memory Management
The cocos2d-x uses the reference count above to manage memory, but adds some of its own features (Auto recycle pool ).
The ref class (ccobject in 2. x) is used in the cocos2d-x to implement reference counting. All classes that require automatic memory reclaim should inherit from the ref class. When an object is created, the reference count is 1, the reference count is 1 when the object is copied, and the reference count is 1 when the object is released. If the reference count is 0, the object memory is released. The following is the definition of the ref class (the asserted statement is removed for simplicity, but its code integrity is not affected ):
Class cc_dll ref {public: // Add the reference count to 1 void retain () {++ _ referencecount;} // subtract 1 from the reference technique. If the reference count is 0, release the current object void release () {-- _ referencecount; If (_ referencecount = 0) {Delete this ;}} // Add the current object to the current automatic recycle pool ref * autorelease () {poolmanager: getinstance ()-> getcurrentpool ()-> addobject (this); return this ;} unsigned int getreferencecount () const {return _ referencecount;} protected: ref (): _ referencecount (1) // when the ref is created, the reference count of it is 1 {} public: Virtual ~ Ref (); protected: // count of references unsigned int _ referencecount; friend class autoreleasepool ;};
There are usually two ways to create objects in a cocos2d-x:
1)auto sprite = new Sprite;2)auto sprite = Sprite::create();Object *Object::create(){ Object *obj = new Object; if (obj && obj->init()) { obj->autorelease(); } else { delete obj; obj = nullptr; } return obj;}
The differences between the two methods can be seen in my other blog article "[cocos2d-x 3.x Study Notes 02] object creation method discussion ". The second approach is promoted in cocos2d-x, And the constructors are generally set to protected or private to avoid misuse of the first approach.
Create () is used to create an object. After the object is created successfully, the autorelease () method of the object is called to add the object to the current automatic recycle pool. When each frame is drawn, all objects in the current automatic recycle pool are traversed to execute the release () method, and then the objects in the automatic recycle pool are cleared. The following function call sequence can be used to locate the point where the automatic recycle pool is cleared when each frame is drawn:
// Main. cppreturn application: getinstance ()-> Run (); // appdelegate. cppint application: Run () {... while (! Glview-> windowshouldclose () {queryperformancecounter (& Nnow); If (Nnow. quadpart-nlast. quadpart> _ animationinterval. quadpart) {nlast. quadpart = Nnow. quadpart; // enter loop ctor-> mainloop (); glview-> pollevents ();} else {sleep (0 );}}...} // ccdirector. cppvoid displaylinkdirector: mainloop () {If (_ purgedirectorinnextloop) {_ purgedirectorinnextloop = false; purgedirector ();} else if (! _ Invalid) {drawscene (); // release the objects poolmanager: getinstance ()-> getcurrentpool ()-> clear () ;}// ccautoreleasepool. cppvoid autoreleasepool: clear () {for (const Auto & OBJ: _ managedobjectarray) {obj-> release ();} _ managedobjectarray. clear (); // STD: vector <cocos2d: ref *> type array}
If an object is not used after it is created using CREATE (), the default reference count is 1. When the first frame of the scenario is drawn, the release () of the object is called () the reference count is reduced by 1 to 0. Because the reference count is 0, the memory occupied by the object is released, and the object is cleared from the automatic recycle pool.
If an object is created by using CREATE () and addchild () is added to a parent node or the retain () function of the object is called directly, the reference count of the object is increased by 1. At the end of the first frame of the scenario, the release () method of the object is called to reduce the reference count by 1, and the object is cleared from the automatic recycle pool. At this time, the reference count of the object is 1, but the object is no longer in the automatic recycle pool. Therefore, it is not affected when the next frame is drawn to clear the automatic recycle pool, the occupied memory space is not automatically released, unless the release () function is called to reduce the reference count by 1 again, so that the reference count is changed to 0 (removechild () or the parent node is released. These operations actually call the release () function of the current child node ).
Therefore, after you create an object using CREATE (), you can either add it to another node or call the retian () function of the object. Otherwise, you can call the Event Callback Function elsewhere) during the call, the reference wild pointer throws an exception because the object is released at the end of a frame.
Best practices
If you want to get the automatic memory management of the cocos2d-x, it is best not to directly use the retain () and release () Functions of the object, if you really need it, you also need to use it in pairs, call a retain () the release () function must be called at an appropriate time and place.
Reference: [1] cocos2d-x advanced development tutorial section 2.3
[2] cocos2d-x 3.x source code
[Cocos2d-x 3.x Study Notes] Object Memory Management