[ruby-core:112476] [Ruby master Bug#19041] Weakref is still alive after major garbage collection

Issue #19041 has been updated by parker (Parker Finch). File manifest_weakref_issue.rb added Hi @tenderlovemaking! I'm having difficulty interpreting the results of the `ObjectSpace` dump and I'm hoping you can help. I've adjusted the script to print out the address of the underlying object, and then (when the issue manifests) print all lines from `ObjectSpace.dump_all` that match that address. The code is attached, here's some example output: ``` Ruby version: 3.3.0 Iteration: 0 Object address: 0x1051cd788 Inner iterations: 1 Iteration: 1 Object address: 0x105205ae8 Inner iterations: 1 Inner iterations: 2 Inner iterations: 3 {"address":"0x105205ae8", "type":"OBJECT", "shape_id":5, "slot_size":40, "class":"0x1029bfe80", "embedded":true, "ivars":0, "memsize":40, "flags":{"wb_protected":true, "old":true, "uncollectible":true, "marked":true}} {"address":"0x10520da90", "type":"STRING", "shape_id":0, "slot_size":40, "class":"0x1029beda0", "embedded":true, "bytesize":11, "value":"0x105205ae8", "encoding":"UTF-8", "coderange":"7bit", "memsize":40, "flags":{"wb_protected":true, "old":true, "uncollectible":true, "marked":true}} ``` In that example, the underlying object was at `0x105205ae8`. But as far as I can tell, there's nothing else that points at it. (The other object there is the String used to hold that address.) I would have expected that, if nothing was referencing it, it would be collected by GC. One interesting tidbit is that just calling `ObjectSpace.dump_all` prevents the issue from manifesting. Is it possible that something _was_ referencing the object address, then running `dump_all` caused that reference to be removed? ---------------------------------------- Bug #19041: Weakref is still alive after major garbage collection https://bugs.ruby-lang.org/issues/19041#change-101919 * 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 ``` ---Files-------------------------------- manifest_weakref_issue.rb (1.52 KB) -- https://bugs.ruby-lang.org/
participants (1)
-
parker (Parker Finch)