Developers Manual > Cocos2d-x > Modules > Scripting > Javascript Binding > Memory Management of JSB

Memory Management of JSB

Base on Cocos2d-x 2.1.5, but also applicable to Cocos2d-x 3.0.

The lifecycle of JSB object

You know Javascript has it’s own memory management, Garbage collection. And Cocos2d-x simulate a garbage collection system to manager Cocos objects. There is a problem that which one is in charge of memory management when binding Cocos2d-x objects to javascript objects.

Let’s look at a typical case.

Object allocation by xxx.create()

The following code allocation a global variable.

gnode = cc.Node.create();

And gnode is not addChild() to other cc.Node.

In a menuItem callback, add following code:

// menuItem callback
onButton:function (sender) {

When click the button, you will see following error message.

Cocos2d: jsb: ERROR: File /Users/u0u0/Documents/project/SK_parkour/scripting/javascript/bindings/generated/jsb_cocos2dx_auto.cpp: Line: 3010, Function: js_cocos2dx_CCNode_addChild
Cocos2d: Invalid Native Object

What happened! What does the “Invalid Native Object” mean?

gnode is a global variable in javascript, mean not be GC.

But the CCNode in side of gnode is GC by Cocos2d-x.

In order to clarify this issue, you need to know spidermonkey and deep into javascript binding codes.

The internal implementation of cc.Node.create()

Detail implementation codes is list as following:

static JSFunctionSpec st_funcs[] = {
    JS_FN("create", js_cocos2dx_CCNode_create, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),

jsb_CCNode_prototype = JS_InitClass(
    cx, global,
    NULL, // parent proto
    js_cocos2dx_CCNode_constructor, 0, // constructor
    NULL, // no static properties

cc.Node.create() is maped to C function js_cocos2dx_CCNode_create()

JSBool js_cocos2dx_CCNode_create(JSContext *cx, uint32_t argc, jsval *vp)
    if (argc == 0) {
        cocos2d::CCNode* ret = cocos2d::CCNode::create();
        jsval jsret;
        do {
        if (ret) {
            js_proxy_t *proxy = js_get_or_create_proxy(cx, ret);
            jsret = OBJECT_TO_JSVAL(proxy->obj);
        } else {
            jsret = JSVAL_NULL;
    } while (0);
        JS_SET_RVAL(cx, vp, jsret);
        return JS_TRUE;
    JS_ReportError(cx, "wrong number of arguments");
    return JS_FALSE;

Object successful allocated by cocos2d::CCNode::create() will be packaged into a new object js_proxy_t which create by js_get_or_create_proxy().

Deeper in js_get_or_create_proxy() only need to focus on following code:

JS_AddObjectRoot(cx, &proxy->obj);

This a spidermonkey api use to add a JSObject to the garbage collector’s root set. proxy->obj is a JSObject map to javascript side.

So objects allocated by cc.Node.create() will remain in memory until the balancing call to JS_RemoveObjectRoot().

But cocos2d::CCNode::create is an autorelease object will be GC in next game frame by Cocos2d-x.

The destructor of CCObject will be call, focus following codes:

// if the object is referenced by Lua engine, remove it
if (m_nLuaID)
    CCScriptEngineProtocol* pEngine = CCScriptEngineManager::sharedManager()->getScriptEngine();
    if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript)

pEngine->removeScriptObjectByCCObject does the magic thing.

void ScriptingCore::removeScriptObjectByCCObject(CCObject* pObj)
    js_proxy_t* nproxy;
    js_proxy_t* jsproxy;
    void *ptr = (void*)pObj;
    nproxy = jsb_get_native_proxy(ptr);
    if (nproxy) {
        JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
        jsproxy = jsb_get_js_proxy(nproxy->obj);
        JS_RemoveObjectRoot(cx, &jsproxy->obj);
        jsb_remove_proxy(nproxy, jsproxy);

Function JS_RemoveObjectRoot remove the JSObject from javascript root set. jsb_remove_proxy will remove proxy from hash table.

Now we can answer the question of the beginning of the article.

Cocos2d-x’s garbage collection system is in charge of memory management.

Back to gnode, it’s a global variable. The effect of JS_RemoveObjectRoot in the destructor of CCObject just balance the JS_AddObjectRoot in create(). Spidermonkey will not GC this global variable, but the native object of gnode had be released. Access the native object of gnode will trigger an error as you saw earlier.

Object allocation by new

Consider the following codes:

gnode = new cc.Node;

To found the true, you also need deeper into JSB code.

As showing earlier, the constructor of cc.Node is the function js_cocos2dx_CCNode_constructor().

Focus following codes:

if (argc == 0) {
    cocos2d::CCNode* cobj = new cocos2d::CCNode();
    cocos2d::CCObject *_ccobj = dynamic_cast(cobj);
    if (_ccobj) {

The native object be puted into Cocos2d-x autorelease pool. So new has no different to create().

About retain() and release()

These two functions are used for manual control object’s lifecycle. If you want to avoid the error in the previous example.

You got two choices:

  1. add gnode to another CCNode, addChild() does retain for gnode internally.
  2. call gnode.retain() right after create().

In the second case, you need call gnode.release() at a right moment to avoid memory leak. The next section will show it.

ctor() and onExit()

Cocos2d-x JSB use Simple JavaScript Inheritance By John Resig. But the name of constructor is different.

ctor() is the constructor in JSB. Instead, onExit() will actor as the destructor be called before CCNode release.

Following example will show how to manual control the lifecycle of JSB object.

var container = cc.Node.extend({
    ctor:function () {
        this.gnode = cc.Node.create();
    onExit:function() {

Sign up for our newsletter to keep up with the latest developments, releases and updates for Cocos2d-x.