Skip to content

Commit 3ec597f

Browse files
committed
Fix memory leak in cloning complex imemo_fields
When we clone a complex imemo_fields, it calls creates the imemo_fields using rb_imemo_fields_new_complex, which allocates and initializes a new st_table. However, st_replace will directly replace any exisiting fields in the st_table, causing it to leak. For example, this script demonstrates the leak: obj = Class.new 8.times do |i| obj.instance_variable_set(:"@test#{i}", nil) obj.remove_instance_variable(:"@test#{i}") end obj.instance_variable_set(:"@test", 1) 10.times do 100_000.times do obj.dup end puts `ps -o rss= -p #{$$}` end Before: 26320 39296 52320 63136 75520 87008 97856 114800 120864 133504 After: 16288 20112 20416 20720 20800 20864 21184 21424 21904 21904
1 parent 5179b7f commit 3ec597f

File tree

1 file changed

+6
-3
lines changed

1 file changed

+6
-3
lines changed

imemo.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,14 @@ rb_imemo_fields_clone(VALUE fields_obj)
167167
VALUE clone;
168168

169169
if (rb_shape_too_complex_p(shape_id)) {
170-
clone = rb_imemo_fields_new_complex(rb_imemo_fields_owner(fields_obj), 0);
171-
RBASIC_SET_SHAPE_ID(clone, shape_id);
172170
st_table *src_table = rb_imemo_fields_complex_tbl(fields_obj);
173-
st_table *dest_table = rb_imemo_fields_complex_tbl(clone);
171+
172+
st_table *dest_table = xcalloc(1, sizeof(st_table));
173+
clone = rb_imemo_fields_new_complex_tbl(rb_imemo_fields_owner(fields_obj), dest_table);
174+
174175
st_replace(dest_table, src_table);
176+
RBASIC_SET_SHAPE_ID(clone, shape_id);
177+
175178
st_foreach(dest_table, imemo_fields_complex_wb_i, (st_data_t)clone);
176179
}
177180
else {

0 commit comments

Comments
 (0)