2016-07-20 2 views
0

this question에서 학습 한 후 힙에 SpiderMonkey의 전역 개체를 저장하려고합니다. 이것은 JS_DestroyRuntime이 호출되기 전에 범위를 벗어나는 한 작동하는 것 같습니다. 그러나 내 코드에서 전역 개체는 클래스 멤버이므로 클래스의 소멸자에서 런타임이 삭제되기 전에 범위를 벗어날 수 없습니다. 여기JS :: Heap에 글로벌 js 개체를 저장하는 중

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 

을 문제를 유발 최소한의 예입니다

불행하게도,이 다음과 같은 스택 추적과 JS::~Heap에서 충돌 원숭이로 연결됩니다. 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

처음 문제는 잘못된 개체 할당 순서로 인해 발생합니다.

C++ 클래스는 구조 역순으로 소멸됩니다. 즉, 클래스 소멸자 다음에 클래스 멤버 소멸자가옵니다. 회원은 선언의 역순으로 파괴됩니다. 그것은 즉, 귀하의 경우 :

  1. ~ *
  2. JS_Destroy를 입력하고 JS_ShutDown는 힙이 호출 :: ~
  3. JS라고() MyMonkeyClass. 그러나 JS 엔진이 이미 사라져서 정의되지 않은 동작이 발생합니다.

m_global을 nullptr로 설정하면 JS :: ~ Heap()이 JS API로 이동하여 메모리를 정리하지 못하게합니다. 그것은 충돌을 회피하지만 메모리 누출 가능성을 남깁니다.

올바른 수정으로 모든 것이 올바른 순서로 정리됩니다.

JS 엔진을 초기화하고 생성자에서 JS 런타임 및 JS 컨텍스트를 생성하고 소멸자에서 JS_Destroy * 및 JS_ShutDown을 호출하는 또 다른 클래스 "JsUpDown"을 정의하는 것이 한 가지 방법입니다. 그런 다음 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; 
};