从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指针指的是仍然在使用的内存,因此它会引起恐慌吗?
答案 0 :(得分:0)
您的初始问题是由对象解除分配的错误顺序引起的。
C ++类以构造反向顺序被破坏,即对类析构函数的调用后跟类成员析构函数。成员在声明相反的命令中被销毁。这意味着在你的情况下:
通过将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;
};