新手问题 Ruby 的 Mark 是怎么回事……

yangff · 2013年06月16日 · 最后由 yangff 回复于 2013年06月16日 · 3936 次阅读

我在 rb_data_object_alloc 的 cmark 那里,对持有的对象进行了 mark,断点也能断到,确实是 mark 了,然后 mark 的这个对象的 cfree 马上就被调用了……

用 rb_gc_mark / rb_gc_mark_maybe 应该不会的啊?

#1 楼 @luikore

void CSprite::Mark(){
    if (disposed) return ;
    if (vectorShader) RB->GcMark(vectorShader->GetValue());
    if (vectorSetting) 
        RB->GcMark(vectorSetting->GetValue()); // 断点1
/*
        virtual void                    GcMark(VALUE obj)                                                               { rb_gc_mark(obj); }

*/
    if (m_texture) RB->GcMark(m_texture->GetValue());
    if (pixelShader) RB->GcMark(pixelShader->GetValue());
    if (pixelSetting) RB->GcMark(pixelSetting->GetValue());
};
    class  SolidVectorShaderSetting : public VectorShaderSetting{
    public:
        void Setup(IDirectX * idx_);
        virtual void DxSetup();
        void update();
        void Resize(int target); // target > 0
        virtual int GetSize();
        SolidVectorShaderSingleSetting * GetVector(int x);
        virtual bool NeedDraw();
        virtual void Mark();
        static bool HaveMarkProc() { return true; }
    protected:
        ID3D11Buffer* vertexBuffer_;
        int size;
        SolidVectorShaderSingleSetting * settings;
        IDirectX * idx;
        bool CanDraw;
    public:
        SolidVectorShaderSetting(){vertexBuffer_ = NULL;CanDraw = false;size = 0;settings = NULL;}
        virtual~SolidVectorShaderSetting(){
            SAFE_DELETE_ARRAY(settings);  // 断点2
            SAFE_RELEASE(vertexBuffer_);
            CanDraw = false;size = 0;
        };
    };

我设置了这两个断点……然后断点 1 和断点 2 分别被断到……这怎么解决。

看不出来... 建议检查一下:

  • 是不是把 *vectorSetting 放栈上了?局部作用域终结会调用析构函数的
  • cmark 和 cfree 顺序没写反吧?
  • cmark 和 cfree 的函数指针是 C-call 而不是 this-call 吧?

#3 楼 @luikore vectorSetting 是用 ruby_xmalloc+rb_data_object_alloc……大概不是这个问题吧,因为 vectorSetting 确实是被 Ruby 的 GC 回收掉了,而不是 C++ 调用的析构函数。这个从断点的栈视图可以看出来。。 cmark 和 cfree 肯定没反……也包装过了。。应该不是后两个的问题。。

#4 楼 @yangff ruby_xmalloc 是堆分配的,但对象还是有可能会拷贝到栈上的,例如不小心写了 VectorShaderSetting aCopy = *vectorSetting; , 然后那个栈上的拷贝的析构就进去了...

还有可能是 CSprite 在栈上有拷贝连锁调用了析构... 确认这个析构是从 cfree 进来的?

#5 楼 @luikore 绝对确定。 RgeDx11.dll!Dx11::SolidVectorShaderSetting::~SolidVectorShaderSetting() 行 63 C++ RgeDx11.dll!Dx11::SolidVectorShaderSetting::`scalar deleting destructor'(unsigned int) C++ RgeDx11.dll!Rge::CppExporter::_rb_freeDx11::SolidVectorShaderSetting(Dx11::SolidVectorShaderSetting * obj_ptr) 行 71 C++ msvcrt-ruby191.dll!62d4f399() 未知 [下面的框架可能不正确和/或缺失,没有为 msvcrt-ruby191.dll 加载符号]
msvcrt-ruby191.dll!62d524ae() 未知 msvcrt-ruby191.dll!62e538bd() 未知

……

话说 xmalloc 出来的对象不是 new 出来的,只能手动调用 destructor 吧?会调用 delete 就很奇怪...

#7 楼 @luikore destructor 确实是手动调用的……也确实是 new 出来的= = ///< 分配内存(对象大小 + 内存标记大小) void* obj_mem = GetIRAL()->XMalloc(sizeof(T) + sizeof(u32)); RgeAssert(obj_mem);

///< 在指定位置构造对象(默认构造函数 不带参数) T* obj_ptr = new (obj_mem) T;

搞不懂... 我的话就从断点 1 开始步进...

ruby 里有些控制 gc 的辅助函数:GC.stop, GC.start 可以帮助 debug

#9 楼 @luikore 知道…… 之所以肯定 GC 有问题的另一个原因就是’‘GC.disable’‘之后世界安静了……

RgeDx11.dll!Dx11::SolidVectorShaderSetting::~SolidVectorShaderSetting() 行 62 C++ RgeDx11.dll!Dx11::SolidVectorShaderSetting::`scalar deleting destructor'(unsigned int) C++ RgeDx11.dll!Rge::CppExporter::rb_freeDx11::SolidVectorShaderSetting(Dx11::SolidVectorShaderSetting * obj_ptr) 行 71 C++ msvcr100-ruby191.dll!finalize_list(rb_objspace * objspace, RVALUE * p) 行 1808 C msvcr100-ruby191.dll!rb_gc_finalize_deferred() 行 2635 C msvcr100-ruby191.dll!rb_threadptr_execute_interrupts_rec(rb_thread_struct * th, int sched_depth) 行 1308 C msvcr100-ruby191.dll!rb_threadptr_execute_interrupts(rb_thread_struct * th) 行 1333 C msvcr100-ruby191.dll!vm_exec_core(rb_thread_struct * th, unsigned long initial) 行 1080 C msvcr100-ruby191.dll!vm_exec(rb_thread_struct * th) 行 1147 C msvcr100-ruby191.dll!invoke_block_from_c(rb_thread_struct * th, const rb_block_struct * block, unsigned long self, int argc, const unsigned long * argv, const rb_block_struct * blockptr, const RNode * cref) 行 558 C msvcr100-ruby191.dll!rb_yield_0(int argc, const unsigned long * argv) 行 740 C msvcr100-ruby191.dll!rb_yield(unsigned long val) 行 750 C msvcr100-ruby191.dll!range_each(unsigned long range) 行 494 C msvcr100-ruby191.dll!call_cfunc(unsigned long (...) * func, unsigned long recv, int len, int argc, const unsigned long * argv) 行 318 C msvcr100-ruby191.dll!vm_call_cfunc(rb_thread_struct * th, rb_control_frame_t * reg_cfp, int num, unsigned long recv, const rb_block_struct * blockptr, const rb_method_entry_struct * me) 行 404 C msvcr100-ruby191.dll!vm_call_method(rb_thread_struct * th, rb_control_frame_t * cfp, int num, const rb_block_struct * blockptr, unsigned long flag, unsigned long id, const rb_method_entry_struct * me, unsigned long recv) 行 524 C msvcr100-ruby191.dll!vm_exec_core(rb_thread_struct * th, unsigned long initial) 行 1006 C msvcr100-ruby191.dll!vm_exec(rb_thread_struct * th) 行 1147 C msvcr100-ruby191.dll!eval_string_with_cref(unsigned long self, unsigned long src, unsigned long scope, RNode * cref, const char * volatile file, volatile int line) 行 1029 C msvcr100-ruby191.dll!rb_f_eval(int argc, unsigned long * argv, unsigned long self) 行 1119 C msvcr100-ruby191.dll!call_cfunc(unsigned long (...) * func, unsigned long recv, int len, int argc, const unsigned long * argv) 行 315 C msvcr100-ruby191.dll!vm_call0(rb_thread_struct * th, unsigned long recv, unsigned long id, int argc, const unsigned long * argv, const rb_method_entry_struct * me) 行 79 C msvcr100-ruby191.dll!rb_call0(unsigned long recv, unsigned long mid, int argc, const unsigned long * argv, call_type scope, unsigned long self) 行 235 C msvcr100-ruby191.dll!rb_funcall2(unsigned long recv, unsigned long mid, int argc, const unsigned long * argv) 行 651 C RgeCore_d.dll!Rge::CRubyAbstractLayer::Funcall(unsigned long obj, unsigned long mid, int argc, ...) 行 575 C++ RgeCore_d.dll!Rge::CCoreRuntime::Require(const char * pScripts, unsigned long uScriptSize, const wchar_t * pFile, const unsigned long uLine) 行 97 C++ RgeCore_d.dll!Rge::CCoreRuntime::CoreRun2() 行 135 C++ RgeCore_d.dll!Rge::CCoreRuntime::CoreRunInProtected(unsigned long argv) 行 105 C++ msvcr100-ruby191.dll!rb_protect(unsigned long (unsigned long) * proc, unsigned long data, int * state) 行 718 C RgeCore_d.dll!Rge::CRubyAbstractLayer::ProtectRun(unsigned long (unsigned long) * proc, unsigned long args, int * state) 行 189 C++ RgeCore_d.dll!Rge::CCoreRuntime::CoreRun(const wchar_t * pScriptsName) 行 198 C++ RgePlayer_d.exe!WinMain(HINSTANCE_ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nShowCmd) 行 170 C++ RgePlayer_d.exe!__tmainCRTStartup() 行 528 C RgePlayer_d.exe!WinMainCRTStartup() 行 377 C

换了个有符号的 Ruby……简直悲伤。

=_= 还没碰到过 gc_mark 了却调到析构的... 我猜这个析构很可能和 gc_mark 没联系...

说不定 CSprite 对应的 ruby 对象生命比 GUI 元素提前结束,无法到达而被艹了...

#11 楼 @luikore gc_mark 的析构是包装的时候手动调用的……问题就是为什么_rb_free 被调用了…… ps 大概 sprite 没机会挂掉……

@sprite.vss.update
while @dx.update
  for j in 0...120
    pos = compressLLPos(rand(800),rand(600),@texture.width,@texture.height)
    for i in 0...6
      @sprite.vss[j*6+i].x = 1#pos[i][0][0]
     # @sprite.vss[j*6+i].y = pos[i][0][1]
     # @sprite.vss[j*6+i].u = pos[i][1][0]
    #  @sprite.vss[j*6+i].v = pos[i][1][1]
    end
  end
  @sprite.vss.update
end
@sprite.dispose
@dx.dispose

你可以打开 GC.stress, 然后在断点 2 rb_eval_string("puts caller"); 看看 ruby 方面的调用栈

#13 楼 @luikore

---------------------------
p
---------------------------
["RgeDx.rb:32:in `compressLLPos'", "RgeDx.rb:60:in `block in <main>'", "RgeDx.rb:59:in `each'", "RgeDx.rb:59:in `<main>'", "RgeCore:in `eval'"]

---------------------------
确定   
---------------------------

我什么都看不懂了……看起来是在 compressLLPos 的时候分配内存然后被回收了……这不科学啊!!

#14 楼 @yangff 不,应该是调用 compressLLPos 之前那一步的问题

#15 楼 @luikore 卧槽……不对,mark 根本没被调用。。

多谢……解决了……

因为各种坑爹的原因,mark 没被调用&#%#¥%#¥%#%

#16 楼 @yangff 应该不会的...

mark 在 1.9.3 之前是设置对象头的一个 flag, 在 1.9.3 之后是在一个 bitset 里设置一个位,mark 完的对象就不会 free.

2.1 之前是用 Knüth 的手工栈法避免递归 mark, 2.1 后优化了手工栈。

不过你可以记录下所有 mark 过的 vectorSetting 的地址,然后和析构里的 this 作对比...

#17 楼 @luikore 不是……因为程序上的原因,包装 C++ 类的东西把它当做没有 mark 吃掉了。。 然后另一边又把它当做有 mark 的…… 所以造成了 mark 被调用的假象,那个 mark 的调用不是吃 Ruby 来的。。

#18 楼 @yangff 好吧... congrats!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号