class Raja
def initialize
ObjectSpace.define_finalizer(self, self.class.finalize(self))
end
def self.finalize(id)
proc do
puts "Object #{id.class} got destroyed"
end
end
def hello
puts 'Raja'
end
end
def hi
obj=Raja.new
obj.hello
end
hi
puts 'Raja'
Consider the preceding program. Because object obj is created inside the hi method and when method call is over, object must have been destroyed, so 'Object #id.class got destroyed' must have been printed, but it's printing after program ends. Why so? Where did my understanding go wrong?
CodePudding user response:
The finalizer gets run when the Garbage Collector collects the object. The Garbage Collector runs when you run out of memory.
Your program only allocates a couple of bytes of memory, so you will never run out of memory and thus the Garbage Collector will never run.
More precisely, the runtime decides when to run the Garbage Collector (and even whether to run the Garbage Collector at all) based on a number of criteria in order to optimize system performance. For example, running the Garbage Collector uses CPU time. So, the runtime tries to find a balance between your program using more memory than strictly necessary (by keeping unreachable objects in memory) and your program using more CPU time than strictly necessary (by running the Garbage Collector even though there is still memory available).
CodePudding user response:
Passing the object itself to the finalize
methods and using it inside the finalizer proc interferes with the garbage collection. As noted in the docs:
Note that if your finalizer references the object to be finalized it will never be run on GC, although it will still be run at exit.
Let's fix that by just passing the object's class instead of the object itself: (you could pass the object itself, but you have to take extra care not to expose it to the proc – passing just the class is easier)
class Raja
def initialize
ObjectSpace.define_finalizer(self, self.class.finalize(self.class))
end
def self.finalize(obj_class)
proc do
puts "Object #{obj_class} got destroyed"
end
end
def hello
puts 'Raja'
end
end
If you now create a Raja
instance, get rid of all references to the object and run garbage collection manually via GC.start
:
obj = Raja.new
obj.hello
obj = nil # destroy reference to Raja instance
GC.start
puts 'Done'
... its output should be as expected:
Raja
Object Raja got destroyed
Done
Note that the garbage collection is implementation specific. It might behave differently for the various Ruby implementations and versions.