Why Cocos Creator Leaks Memory on iOS and How to Fix It
This article investigates the memory‑leak issue in Tencent Penguin Tutor's Cocos Creator implementation on iOS, explains why the engine's node pool and DragonBones animations cause unreleased Texture2D objects, and provides a step‑by‑step solution using explicit pool clearing and proper object disposal.
Tencent Penguin Tutor embeds Cocos Creator for in‑class interactive exercises. Launching the Cocos engine in a separate process avoids v8 initialization errors on Android, but iOS cannot use multiple processes, so the engine runs in the main process and still leaks memory after closing exercises.
Performance tests show memory rising from 126 MB at app start to 252 MB after entering a live room, then increasing slightly with each interactive exercise. Even after earlier optimizations (texture compression, lower‑resolution images, reduced DragonBones animations, node pools), a gradual leak persists.
Initial memory spikes are expected because the Cocos engine itself occupies over 100 MB, but subsequent incremental growth indicates a leak. The leak is not in the JavaScript heap (JS memory stays at ~28 MB), suggesting the problem lies in the native C++ layer.
Investigation points to the Texture2D::setImage method, which copies image data to GPU memory (shared with CPU on iOS) and allocates native memory. The engine registers C++ classes to JavaScript via JSB bindings, e.g., js_register_gfx_Texture2D, and defines lifecycle functions like js_cocos2d_renderer_Texture2D_finalize that call release() on the native object.
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
#define SE_ENABLE_INSPECTOR 1
#define SE_DEBUG 2
#else
#define SE_ENABLE_INSPECTOR 0
#define SE_DEBUG 0
#endifWhen a JS Texture2D object is garbage‑collected, its native counterpart is destroyed, calling glDeleteTextures in the Texture::~Texture destructor. However, leaked Texture2D objects remain because some JS objects are never collected.
The leak appears only during the 1v1PK oral‑practice mode, where a video of the opponent is displayed. The video is rendered natively on iOS, and the associated texture allocation in Texture2D::setImage is not released.
Further analysis reveals that the ripple animation (a DragonBones animation) used during recording playback creates many nodes via cc.NodePool. Although the pool should be cleared automatically, the documentation states that if nodes are still referenced, memory may leak. Manually calling clear() on the node pool eliminates the leak.
DragonBones components hold Armature objects, which in turn own Texture2D instances. When a scene is destroyed, calling node.destroy() triggers Armature.dispose(), which buffers the object for later release via DragonBones::bufferObject and finally frees it in DragonBones::advanceTime.
Therefore, the root cause is the Cocos Creator node pool not being explicitly cleared after scene destruction, leaving native Texture2D objects (especially those created by DragonBones) unreleased.
Solution: In scene cleanup, call nodePool.clear() to destroy pooled nodes and ensure their native resources are freed.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
