galapagosit’s blog

本家 > http://galapagosit.com/

Cocos2d-JSのガベージコレクションについて

Cocos2d-JSでSQLite - galapagosit’s blog

上記の実装をしていた際に気になったこと。

var accesser = new jsb.SQLiteBridge("My.db");
accesser.execSql(sql);

// この後accesserは使用しないものとする

jsバインディングで生成したSQLiteBridgeインスタンス、ちゃんとdeleteされてメモリが開放されるのだろうか?
であればどこかのタイミングで下記の処理が呼ばれるはずだが、呼ばれていない。

SQLiteBridge::~SQLiteBridge()
{
    CCLOG("SQLiteBridge::~SQLiteBridge");
    sqlite3_close(useDataBase);
}

調査してみた

コンストラクタ

そもそもコンストラクタはgenerator.pyによって生成された以下のコードで実行されていた。

bool js_jsb_sqlite_bridge_auto_SQLiteBridge_constructor(JSContext *cx, uint32_t argc, jsval *vp)
{
    jsval *argv = JS_ARGV(cx, vp);
    bool ok = true;
    std::string arg0;
    ok &= jsval_to_std_string(cx, argv[0], &arg0);
    JSB_PRECONDITION2(ok, cx, false, "js_jsb_sqlite_bridge_auto_SQLiteBridge_constructor : Error processing arguments");
    SQLiteBridge* cobj = new (std::nothrow) SQLiteBridge(arg0);
    TypeTest<SQLiteBridge> t;
    js_type_class_t *typeClass = nullptr;
    std::string typeName = t.s_name();
    auto typeMapIter = _js_global_type_map.find(typeName);
    CCASSERT(typeMapIter != _js_global_type_map.end(), "Can't find the class type!");
    typeClass = typeMapIter->second;
    CCASSERT(typeClass, "The value is null.");
    JSObject *obj = JS_NewObject(cx, typeClass->jsclass, typeClass->proto, typeClass->parentProto);
    JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
    // link the native object with the javascript object
    js_proxy_t* p = jsb_new_proxy(cobj, obj);
    if (JS_HasProperty(cx, obj, "_ctor", &ok))
        ScriptingCore::getInstance()->executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), "_ctor", argc, argv);
    return true;
}
デストラクタ

ではデストラクタは何処だろうか、おそらく上記と同一コード内の以下の処理

void js_SQLiteBridge_finalize(JSFreeOp *fop, JSObject *obj) {
    CCLOGINFO("jsbindings: finalizing JS object %p (SQLiteBridge)", obj);
    js_proxy_t* nproxy;
    js_proxy_t* jsproxy;
    jsproxy = jsb_get_js_proxy(obj);
    if (jsproxy) {
        nproxy = jsb_get_native_proxy(jsproxy->ptr);

        SQLiteBridge *nobj = static_cast<SQLiteBridge *>(nproxy->ptr);
        if (nobj)
            delete nobj;
        
        jsb_remove_proxy(nproxy, jsproxy);
    }
}
デストラクタはいつ呼ばれるのか

ソースを追ってみるとおそらく該当のjs変数(accesser)がガベージコレクションされるタイミングの様だ。
で、それっていつなの?デストラクタが呼ばれないと不安で夜も眠れないので意地でも呼ばれるところを見届けたいところです。

どうやら下記のコードで強制的にガベージコレクション出来るようです。

__jsc__.garbageCollect();

これを呼んだ所、ちゃんとデストラクタが呼ばれました。良かったよかった。
そもそもの疑問の答えとしては、ガベージコレクションのタイミングはSpiderMonkeyが制御しているので、すぐには呼ばれなかったということだと思います。