在JS :: Heap <t>中存储全局js对象会在JS_DestroyRuntime中引发崩溃,如果仍在范围内

时间:2016-07-20 12:40:05

标签: memory-management spidermonkey

this question学习后, 我正在尝试将SpiderMonkey的全局对象存储在堆上。只要在调用JS_DestroyRuntime之前它超出范围,这似乎就有效。但是在我的代码中,全局对象是一个类成员,因此在类的析构函数中销毁运行时之前不能超出范围。

不幸的是,这导致Monkey在JS::~Heap中崩溃,并带有以下堆栈跟踪:

1  js::gc::Cell::storeBuffer() const                                                    Heap.h       1339 0x10004f905 
2  JSObject::writeBarrierPost(void *, JSObject *, JSObject *)                           jsobj.h      655  0x1000a6fc8 
3  js::InternalGCMethods<JSObject *>::postBarrier(JSObject * *, JSObject *, JSObject *) Barrier.h    254  0x1000a6df0 
4  JS::HeapObjectPostBarrier(JSObject * *, JSObject *, JSObject *)                      Barrier.cpp  173  0x100bc1636 
5  js::GCMethods<JSObject *>::postBarrier(JSObject * *, JSObject *, JSObject *)         RootingAPI.h 551  0x100003065 
6  JS::Heap<JSObject *>::post(JSObject * const&, JSObject * const&)                     RootingAPI.h 271  0x10000302b 
7  JS::Heap<JSObject *>::~Heap()                                                        RootingAPI.h 237  0x10000369e 
8  JS::Heap<JSObject *>::~Heap()                                                        RootingAPI.h 236  0x100002f75 
9  MyMonkeyClass::~MyMonkeyClass()                                                      main.cpp     64   0x100003725 
10 MyMonkeyClass::~MyMonkeyClass()                                                      main.cpp     58   0x100002aa5 
11 main                                                                                 main.cpp     110  0x100002a12 
12 start                                                                                                  0x1000029d4 

这是触发问题的最小示例。我故意放弃了GC跟踪调用,他们没有改变结果。

#include <js/Initialization.h>
#include <jsapi.h>
#include <QDebug>

// The class of the global object. Just a dummy.
static JSClass global_class = {
    "global",
    JSCLASS_GLOBAL_FLAGS,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    JS_GlobalObjectTraceHook
};

class MyMonkeyClass
{
public:
    MyMonkeyClass() {
        Q_ASSERT( JS_Init() );
        auto runtime = JS_NewRuntime( 5 * 1024 * 1024 /*heap space*/ );
        Q_ASSERT( runtime );
        m_context = JS_NewContext( runtime, 8192 /*default. keep.*/ );
        Q_ASSERT( m_context );

        JSAutoRequest ar( m_context );

        // Not sure which of these alternatives is the correct way with JS::Heap
        m_global = JS::RootedObject( m_context,
                                   JS_NewGlobalObject( m_context, &global_class, nullptr, JS::FireOnNewGlobalHook ) );
        //global = JS_NewGlobalObject( context, &global_class, nullptr, JS::FireOnNewGlobalHook );
        Q_ASSERT( m_global.get() );
    }

    ~MyMonkeyClass() {
        auto runtime = JS_GetRuntime( m_context );
        JS_DestroyContext( m_context );
        JS_DestroyRuntime( runtime );
        JS_ShutDown();
    }

private:
    JSContext *m_context;
    JS::Heap<JSObject*> m_global;
};

int main( int, char** )
{
    MyMonkeyClass mmc;
    return 0;
}

在撰写这个问题时,我发现在破坏事物之前在dtor中设置m_global = nullptr;实际上避免了崩溃。现在我的最后一个问题是:

这是正确的解决方法吗?如果是,为什么? SM可能会假设一个非null的JS :: Heap指针指的是仍然在使用的内存,因此它会引起恐慌吗?

1 个答案:

答案 0 :(得分:0)

您的初始问题是由对象解除分配的错误顺序引起的。

C ++类以构造反向顺序被破坏,即对类析构函数的调用后跟类成员析构函数。成员在声明相反的命令中被销毁。这意味着在你的情况下:

  1. 〜输入MyMonkeyClass()
  2. JS_Destroy *和JS_ShutDown被称为
  3. JS :: ~Heap被调用。但是,JS引擎已经消失,导致未定义的行为 - 在您的情况下崩溃。
  4. 通过将m_global设置为nullptr,可以防止JS ::〜Heap()进入JS API来清理内存。这可以避免崩溃,但是你可能会发生内存泄漏。

    正确的解决方法是按正确的顺序清理所有内容。

    一种方法是定义另一个类&#34; JsUpDown&#34;初始化JS引擎,在构造函数中创建JS运行时和JS上下文,并在析构函数中调用JS_Destroy *和JS_ShutDown。然后MyMonkeyClass看起来像:

    class MyMonkeyClass
    {
      MyMonkeyClass() : m_engine(), m_global() // just to make it explicit
      {
         JSAutoRequest ar(m_engine.m_context);
         m_global = JS::RootedObject(...);
      }
      ~MyMonkeyClass() {} // no need for the destructor now
    
      JsUpDown m_engine;  // Should be first. The order is important:
                          // Members are destroyed in reverse order.
      JS::Heap<JSObject*> m_global;
    };