Project

General

Profile

Bug #15121

Memory Leak on rb_id2name(SYM2ID(sym))

Added by william101 (William Tabi) 9 days ago. Updated 6 days ago.

Status:
Rejected
Priority:
Normal
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

History

#1 [ruby-core:89015] Updated by jeremyevans0 (Jeremy Evans) 9 days 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

#2 [ruby-core:89056] Updated by ko1 (Koichi Sasada) 6 days 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.

Also available in: Atom PDF