Project

General

Profile

Actions

Bug #15121

closed

Memory Leak on rb_id2name(SYM2ID(sym))

Added by william101 (William Tabi) over 5 years ago. Updated over 5 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
ruby -v:
ruby 2.4.4p296 (2018-03-28 revision 63013) [x86_64-darwin17]
[ruby-core:89014]

Description

@ohler55 mentioned in https://github.com/ohler55/oj/issues/501 that calling rb_id2name(SYM2ID(sym)) seems to lock symbols in memory but I couldn't find any issue open for that. So I'm just opening one just in case, but pls close if this is a dupe.

I created a sample C extension to reproduce this

#include "extconf.h"
#include <stdlib.h>
#include <stdio.h>
#include <ruby.h>

VALUE
rb_leak_sym(VALUE self, VALUE argument1) {
    const char	*sym = rb_id2name(SYM2ID(argument1));
    return Qnil;
}

void Init_testsym()
{
    rb_define_global_function("leak_sym", rb_leak_sym, 1);
}

We can see it leaking memory with this snippet

require "testsym"
require "objspace"

def record_allocation
  GC.start
  GC.start

  puts "Before - Objects count: #{ObjectSpace.each_object.count}"
  puts "Before - Symbols count: #{Symbol.all_symbols.size}"

  yield

  GC.start
  GC.start

  puts "After - Objects count: #{ObjectSpace.each_object.count}"
  puts "After - Symbols count: #{Symbol.all_symbols.size}"
end

def leak_symbols
  1_000_000.times.each { |i| leak_sym("string_number_#{i}".to_sym) }
end

record_allocation do
  leak_symbols
end

Output:

$ ruby -v test.rb
ruby 2.4.4p296 (2018-03-28 revision 63013) [x86_64-darwin17]
Before - Objects count: 8784
Before - Symbols count: 3063
After - Objects count: 2008786
After - Symbols count: 1003063

Updated by jeremyevans0 (Jeremy Evans) over 5 years ago

I believe this is expected. Once you ask for the ID of a dynamic symbol, Ruby needs to create a real symbol, and once the real symbol is created, it can never be garbage collected.

You can replace your leak_sym method with the following and observe the same behavior (symbols that appear in ruby code are also real symbols):

def leak_sym(sym)
  eval sym.inspect
end

Updated by ko1 (Koichi Sasada) over 5 years ago

  • Status changed from Open to Rejected

Yes, it is expected.

We separate all symbols into two categories:

  • static symbols: used by method name and so on. They are not GC'ed managed.
  • dynamic symbols: created by Ruby program like str.to_sym.

Static symbols are used as method name and so on. And SYM2ID() makes static symbols from dynamic symbols.
This is an implementation limitation and we expect this limitation is appropriate for most of Ruby programs.

Actions

Also available in: Atom PDF

Like0
Like0Like0