Why is this unused line not collecting garbage?

Why do unused_variable_2 and unused_variable_3 get garbage collection but not unused_variable_1?

# leaky_boat.rb require "memprof" class Boat def initialize(string) unused_variable1 = string[0...100] puts unused_variable1.object_id @string = string puts @string.object_id end end class Rocket def initialize(string) unused_variable_2 = string.dup puts unused_variable_2.object_id unused_variable_3 = String.new(string) puts unused_variable_3.object_id @string = string puts @string.object_id end end Memprof.start text = "a" * 100 object_id_message = "Object ids of unused_variable_1, @string, unused_variable_2, unused_variable_3, and another @string" before_gc_message = "Before GC" after_gc_message = "After GC" puts object_id_message boat = Boat.new(text) rocket = Rocket.new(text) puts before_gc_message Memprof.stats ObjectSpace.garbage_collect puts after_gc_message Memprof.stats Memprof.stop 

Launching the program:

 $ uname -a Linux [redacted] 3.2.0-25-generic #40-Ubuntu SMP Wed May 23 20:30:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux $ ruby --version # Have to use Ruby 1.8 - memprof doesn't work on 1.9 ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux] $ ruby -rubygems leaky_boat.rb Object ids of unused_variable_1, @string, unused_variable_2, unused_variable_3, and another @string 70178323299180 70178323299320 70178323299100 70178323299060 70178323299320 Before GC 2 leaky_boat.rb:6:String 2 leaky_boat.rb:26:String 1 leaky_boat.rb:9:String 1 leaky_boat.rb:7:String 1 leaky_boat.rb:32:Rocket 1 leaky_boat.rb:31:Boat 1 leaky_boat.rb:29:String 1 leaky_boat.rb:28:String 1 leaky_boat.rb:27:String 1 leaky_boat.rb:20:String 1 leaky_boat.rb:18:String 1 leaky_boat.rb:17:String 1 leaky_boat.rb:16:String 1 leaky_boat.rb:15:String After GC 1 leaky_boat.rb:6:String 1 leaky_boat.rb:32:Rocket 1 leaky_boat.rb:31:Boat 1 leaky_boat.rb:29:String 1 leaky_boat.rb:28:String 1 leaky_boat.rb:27:String 1 leaky_boat.rb:26:String 
+4
source share
1 answer

This is because the string implementation of your version of ruby ​​for substr has a special case for preserving memory allocations when you take substr, which is the tail of the original string, and the length of the string is long enough not to store the string value in the structure of the base object.

If you trace the code, you will see that the index of the range string[0...100] will go through this section in rb_str_substr . Thus, a new line will be highlighted through str_new3 , which allocates a new object structure (therefore object_id is different), but sets the string value of the ptr field as a pointer to the extended storage of the original object and sets the ELTS_SHARED flag to indicate that the new object ELTS_SHARED storage with another object.

In your code, you take this new substring object and assign it to the var @string , which is still a live link when you start garbage collection. Since there is a live link to the dedicated storage of the source string, it cannot be compiled.

In the ruby ​​trunk, this optimization for sharing storage on compatible tail substrings seems to still exist.

The other two vars unused_variable_2 and unused_variable_3 do not have this extended repository sharing problem, because they are installed using mechanisms that provide different repository, so they collect garbage, as expected, when their links disappear from the scope.

Line # dup runs rb_str_replace (via initialize_copy binding ), which replaces the contents of the original string with a copy of the contents of the original string and ensures that the storage is not used.

String # new (source_str) goes through rb_str_init , which likewise provides excellent storage with rb_str_replace at the original initial value.

+6
source

All Articles