Bug #15088
closedLeaky behaviour when GC disabled
Description
Hello
I've observed some leaky behaviour when creating dynamic Symbols ("string".to_sym) when GC is disabled. Creating a dynamic symbol after running GC.disable
creates an extra string allocation that isn't garbage collected when GC is enabled later on in the code.
Based on https://bugs.ruby-lang.org/issues/9634 and https://bugs.ruby-lang.org/issues/7791, I would have expected all the memory allocated by #to_sym to be GC'ed
I can reproduce this using
# frozen_string_literal: true
require 'objspace'
GC.start
puts"Before - All: #{ObjectSpace.each_object.count}"
puts"Before - Symbols: #{Symbol.all_symbols.size}"
GC.start
GC.disable
100_000.times do |i|
"to_sym_#{i}".to_sym
end
GC.enable
GC.start
puts"After - All: #{ObjectSpace.each_object.count}"
puts"After - Symbols: #{Symbol.all_symbols.size}"
This is the output:
with latest master v2.6.0-dev
$ RBENV_VERSION=2.6.0-dev ruby -v /tmp/test.rb
ruby 2.6.0dev (2018-09-07 trunk 64657) [x86_64-darwin17]
Before - All: 10740
Before - Symbols: 3206
After - All: 110739
After - Symbols: 3206
with ruby v2.2.2
$ RBENV_VERSION=2.2.2 ruby -v /tmp/test.rb
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin17]
Before - All: 6342
Before - Symbols: 2536
After - All: 106344
After - Symbols: 2536
Would anyone know if this is expected behaviour or a bug?
Updated by normalperson (Eric Wong) over 5 years ago
liamtabi@gmail.com wrote:
100_000.times do |i|
"to_sym_#{i}".to_sym
endGC.enable
GC.start
You need at least another call to GC.start, here.
I think it is because global_symbols.dsymbol_fstr_hash (in
symbol.c) is a GC root, so rb_gc_free_dsymbol needs to be called
to remove the string from dsymbol_fstr_hash by the first
GC.start before the underlying fstring object in
vm->frozen_strings can be freed by the second GC.start call.
Updated by ko1 (Koichi Sasada) over 5 years ago
- Status changed from Open to Rejected
Thank you Eric.
This is my 2nd experiment:
...
# adding the following code to original trial code.
GC.start
puts"After2 - All: #{ObjectSpace.each_object.count}"
puts"After2 - Symbols: #{Symbol.all_symbols.size}"
And I got:
ruby 2.6.0dev (2018-08-13 trunk 64328) [x64-mswin64_140]
Before - All: 10727
Before - Symbols: 3170
After - All: 110729
After - Symbols: 3170
After2 - All: 10731
After2 - Symbols: 3170
Updated by william101 (William Tabi) over 5 years ago
Thank you @normalperson (Eric Wong) & @ko1 (Koichi Sasada) for taking a look and for the explanation here 😀