Issue #21710 has been updated by ivoanjo (Ivo Anjo).
I'm fine with adding MOVEOBJ, it makes sense, but I don't think it's currently necessary. ObjectSpace.trace_object_allocations handles this by subscribing both to NEWOBJ and FREEOBJ and tracking the objects in its own hash table (subscribing to FREEOBJ giving it weakref). It then has a mostly normal compact routine to move the objects https://github.com/ruby/ruby/blob/master/ext/objspace/object_tracing.c#L200-... (although the rb_gc_pointer_to_heap_p is odd, I believe that's only necessary after the object tracing has finished). I used the same technique in Vernier's object tracing and it seems to work fine with compaction.
I think this is a problem for Datadog's profiler vs the other users because it (to my understanding) does not subscribe to FREEOBJ and do its own bookkeeping. So it wants a way for Ruby to handle weakrefs for it (#21722)
Very true! So to clarify, the main reason we took this approach is to try to minimize overhead impact on applications. Having the `NEWOBJ` tracepoint active all the time in production does already by itself add overhead, and that's even with a as-simple-as-possible [skip most objects](https://github.com/DataDog/dd-trace-rb/blob/master/ext/datadog_profiling_nat...) check. The addition of a `FREEOBJ` that for every single object needs to look up a hashtable is a cost that we were hoping to avoid if we can 😀 (We also had a smaller worry that `FREEOBJ` could sometimes be skipped and thus this would become a source of memory leaks, there were some comments in the VM code about it, but unclear if that's something that happens in practice or not)
In 3.3 and below if you call it enough you will eventually miss a write barrier and eventually crash.
I'd very much like to know more about this -- if you have the time/ability to share any details (even privately?) 🙏 For what it's worth, we run a lot of tests with 3.3 and below and we've not spotted the issue so far so I wonder if we've just been lucky or there's something we can do to help there... ---------------------------------------- Bug #21710: Segfault when reading object_id after it is set inside RUBY_INTERNAL_EVENT_NEWOBJ https://bugs.ruby-lang.org/issues/21710#change-115455 * Author: ivoanjo (Ivo Anjo) * Status: Closed * Target version: 4.0 * ruby -v: ruby 4.0.0dev (2025-11-24T08:44:28Z master aeb7689e69) +PRISM [x86_64-linux] * Backport: 3.2: DONTNEED, 3.3: DONTNEED, 3.4: DONTNEED ---------------------------------------- Hey 👋. I caught a following segfault when running the [Datadog Ruby Profiler](https://github.com/datadog/dd-trace-rb) test suite on 4.0.0-preview2. The Datadog Ruby Profiler still uses object_ids in `RUBY_INTERNAL_EVENT_NEWOBJ` to simulate weak references. (We know this is deprecated and we plan to move to [`rb_gc_mark_weak`](https://bugs.ruby-lang.org/issues/19783) in the future). I was able to reproduce this bug with both 4.0.0-preview2 as well as a ruby-head build from 2025-11-24. Here's a [very simple reproducer](https://github.com/ivoanjo/lowlevel-toolkit/pull/1): ```c static void on_newobj_event(VALUE tpval, void *data) { VALUE obj = rb_tracearg_object(rb_tracearg_from_tracepoint(tpval)); if (!rb_objspace_internal_object_p(obj)) rb_obj_id(obj); } static VALUE add_object_id(RB_UNUSED_VAR(VALUE _)) { VALUE tp = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, on_newobj_event, NULL); rb_tracepoint_enable(tp); rb_yield(Qnil); rb_tracepoint_disable(tp); return Qnil; } ``` ```ruby puts RUBY_DESCRIPTION require "lowlevel-toolkit" foo = Struct.new(:foo) bar = nil LowlevelToolkit.add_object_id { bar = foo.new(1) } bar.object_id ``` and here's the segfault: ``` examples/add_object_id.rb:8: [BUG] Segmentation fault at 0x0000000000000000 ruby 4.0.0dev (2025-11-24T08:44:28Z master aeb7689e69) +PRISM [x86_64-linux] -- Control frame information ----------------------------------------------- c:0003 p:---- s:0012 e:000011 l:y b:---- CFUNC :object_id c:0002 p:0044 s:0008 E:002170 l:n b:---- EVAL examples/add_object_id.rb:8 [FINISH] c:0001 p:0000 s:0003 E:001dc0 l:y b:---- DUMMY [FINISH] -- Ruby level backtrace information ---------------------------------------- examples/add_object_id.rb:8:in '<main>' examples/add_object_id.rb:8:in 'object_id' -- Threading information --------------------------------------------------- Total ractor count: 1 Ruby thread count for this ractor: 1 -- Machine register context ------------------------------------------------ RIP: 0x000079c33b33e2cd RBP: 0x00007ffdcee65d10 RSP: 0x00007ffdcee65cd0 RAX: 0x0000000000000000 RBX: 0x000079c33a4ff058 RCX: 0x000079c339000000 RDX: 0x0000000000000000 RDI: 0x0000000000000000 RSI: 0x00000000000fe000 R8: 0x000079c33b35c078 R9: 0x000079c31f2814d0 R10: 0x000058e4893a6ee0 R11: 0xb84a1fbe2b4aab11 R12: 0x0000000000000002 R13: 0x0000000000000000 R14: 0x000058e489384b60 R15: 0x000079c33a5fefa0 EFL: 0x0000000000010246 -- C level backtrace information ------------------------------------------- .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_print_backtrace+0x24) [0x79c33b392149] .rvm/src/ruby-head/vm_dump.c:1105 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_vm_bugreport+0x33f) [0x79c33b39291b] .rvm/src/ruby-head/vm_dump.c:1450 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_bug_for_fatal_signal+0x147) [0x79c33b13a3e2] .rvm/rubies/ruby-head/lib/libruby.so.4.0(sigsegv+0x84) [0x79c33b2bea4d] .rvm/src/ruby-head/signal.c:948 .rvm/rubies/ruby-head/lib/libruby.so.4.0(sigill) (null):0 /lib/x86_64-linux-gnu/libc.so.6(0x79c33ac45330) [0x79c33ac45330] .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_obj_field_get+0x105) [0x79c33b33e2cd] .rvm/src/ruby-head/variable.c:1412 .rvm/rubies/ruby-head/lib/libruby.so.4.0(object_id_get+0x4e) [0x79c33b16ed28] .rvm/src/ruby-head/gc.c:1875 .rvm/rubies/ruby-head/lib/libruby.so.4.0(object_id0+0x46) [0x79c33b16ed78] .rvm/src/ruby-head/gc.c:1895 .rvm/rubies/ruby-head/lib/libruby.so.4.0(object_id+0xb1) [0x79c33b16ee93] .rvm/src/ruby-head/gc.c:1936 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_find_object_id+0x43) [0x79c33b16f6ea] .rvm/src/ruby-head/gc.c:2179 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_obj_id+0x2e) [0x79c33b16f75a] .rvm/src/ruby-head/gc.c:2234 .rvm/rubies/ruby-head/lib/libruby.so.4.0(ractor_safe_call_cfunc_0+0x30) [0x79c33b35c0a8] .rvm/src/ruby-head/vm_insnhelper.c:3718 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_cfunc_with_frame_+0x221) [0x79c33b35ccb1] .rvm/src/ruby-head/vm_insnhelper.c:3902 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_cfunc_with_frame+0x76) [0x79c33b35cf26] .rvm/src/ruby-head/vm_insnhelper.c:3948 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_cfunc_other+0x12b) [0x79c33b35d053] .rvm/src/ruby-head/vm_insnhelper.c:3974 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_cfunc+0x147) [0x79c33b35d49a] .rvm/src/ruby-head/vm_insnhelper.c:4056 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_method_each_type+0x180) [0x79c33b360161] .rvm/src/ruby-head/vm_insnhelper.c:4888 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_method+0xa1) [0x79c33b360c1d] .rvm/src/ruby-head/vm_insnhelper.c:5014 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_call_general+0x2f) [0x79c33b360e1f] .rvm/src/ruby-head/vm_insnhelper.c:5058 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_sendish+0x1d3) [0x79c33b363689] .rvm/src/ruby-head/vm_insnhelper.c:6124 .rvm/rubies/ruby-head/lib/libruby.so.4.0(vm_exec_core+0x3b1d) [0x79c33b36b42c] .rvm/src/ruby-head/insns.def:903 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_vm_exec+0x140) [0x79c33b384d94] .rvm/src/ruby-head/vm.c:2784 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_iseq_eval_main+0x3d) [0x79c33b385be2] .rvm/src/ruby-head/vm.c:3050 .rvm/rubies/ruby-head/lib/libruby.so.4.0(rb_ec_exec_node+0x128) [0x79c33b145eb4] .rvm/src/ruby-head/eval.c:283 .rvm/rubies/ruby-head/lib/libruby.so.4.0(ruby_run_node+0x8a) [0x79c33b146025] .rvm/src/ruby-head/eval.c:321 .rvm/rubies/ruby-head/bin/ruby(rb_main+0x4c) [0x58e47500e51e] ./main.c:42 .rvm/rubies/ruby-head/bin/ruby(main+0x62) [0x58e47500e596] ./main.c:62 ``` Let me know if I can provide any more info! ---Files-------------------------------- bug-21710.patch (1.25 KB) -- https://bugs.ruby-lang.org/