From: "parker (Parker Finch)" Date: 2022-10-14T20:04:21+00:00 Subject: [ruby-core:110292] [Ruby master Bug#19041] Weakref is still alive after major garbage collection Issue #19041 has been updated by parker (Parker Finch). Thanks for that explanation @tenderlovemaking, it helps and I truly appreciate it! One misunderstanding I had was that I was thinking about this in terms of the Ruby VM. But it seems like garbage collection actually occurs down at the machine level (which makes much more sense now that I think about it) and that's why we're dealing with registers. (And the stack we're talking about is the C stack and not the Ruby VM stack.) The recommendation to take a look at [RB_GC_GUARD](https://github.com/ruby/ruby/blob/cbd3d655745564e3c33a29a5625ac30b4d69fb29/include/ruby/internal/memory.h#L110-L172) was helpful as well, that's a great comment there. I'm still curious _why_ calling `#weakref_alive?` on the `WeakRef` seems to put the underlying `Object` (that the `WeakRef` delegates to) in a register or on the stack. But the fact that this is happening so close to the actual machine makes it seem like it would be tricky to figure out. Anyway, I'll keep learning more about how memory management works, thank you for the info here! I think the docs are fine as-is, so it makes sense to me to close this one. Thank you all for your time and explanations! ---------------------------------------- Bug #19041: Weakref is still alive after major garbage collection https://bugs.ruby-lang.org/issues/19041#change-99577 * Author: parker (Parker Finch) * Status: Closed * Priority: Normal * ruby -v: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin21] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- I am able to get into an infinite loop waiting for garbage collection to take a WeakRef. ### Reproduction Process The following script prints a "0", then a "1", and then hangs forever. I expect it to keep printing numbers. ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) GC.start while obj.weakref_alive? iterations += 1 end ``` ### Ruby Version I have tested this on Ruby 3.1.2, 3.1.0, 3.0.4, 3.0.0, 2.7.6, and 2.7.0 on macOS. All exhibit this behavior. ### Further Investigation #### Sleeping Sleeping before the garbage collection allows the loop to continue. The below exhibits the expected behavior: ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) (sleep(0.5); GC.start) while obj.weakref_alive? iterations += 1 end ``` However, sleeping _after_ the garbage collection still shows the buggy behavior (loop hangs): ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) (GC.start; sleep(0.5)) while obj.weakref_alive? iterations += 1 end ``` #### Running Garbage Collection Multiple Times Explicitly running garbage collection multiple times allows the loop to continue. This has the expected behavior, more numbers continue to be printed: ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) while obj.weakref_alive? GC.start GC.start GC.start end iterations += 1 end ``` However, with certain rubies, running those garbage collection calls in a `times` block prevents even a single iteration from completing. The following prints only "0" with ruby 3.0.4 on macOS, ruby 2.7.6 on macOS, and ruby 3.1.2 on linux (`ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]` on a virtual machine). It shows the expected behavior on ruby 3.1.2 on macOS. ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) 3.times { GC.start } while obj.weakref_alive? iterations += 1 end ``` -- https://bugs.ruby-lang.org/ Unsubscribe: