Skip to content

Commit

Permalink
Fix memory leak for iclass
Browse files Browse the repository at this point in the history
[Bug #19550]

If !RCLASS_EXT_EMBEDDED (e.g. 32 bit systems) then the rb_classext_t is
allocated throug malloc so it must be freed.

The issue can be seen in the following script:

```
20.times do
  100_000.times do
    mod = Module.new
    Class.new do
      include mod
    end
  end

  # Output the Resident Set Size (memory usage, in KB) of the current Ruby process
  puts `ps -o rss= -p #{$$}`
end
```

Before this fix, the max RSS is 280MB, while after this change, it's
30MB.
  • Loading branch information
peterzhu2118 committed Mar 28, 2023
1 parent 6ce6b4d commit 417b1a3
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 1 deletion.
2 changes: 1 addition & 1 deletion gc.c
Expand Up @@ -3677,7 +3677,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
cc_table_free(objspace, obj, FALSE);
rb_class_remove_from_module_subclasses(obj);
rb_class_remove_from_super_subclasses(obj);
#if !USE_RVARGC
#if !RCLASS_EXT_EMBEDDED
xfree(RCLASS_EXT(obj));
#endif

Expand Down
15 changes: 15 additions & 0 deletions test/ruby/test_module.rb
Expand Up @@ -3276,6 +3276,21 @@ def test_module_name_in_singleton_method
assert_match(/::Foo$/, mod.name, '[Bug #14895]')
end

def test_iclass_memory_leak
# [Bug #19550]
assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
code = proc do
mod = Module.new
Class.new do
include mod
end
end
1_000.times(&code)
PREP
3_000_000.times(&code)
CODE
end

private

def assert_top_method_is_private(method)
Expand Down

0 comments on commit 417b1a3

Please sign in to comment.