From a615885f1e87f4bfbc5398b060fd3a64d5de8c4a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 29 Aug 2021 16:47:26 +0900 Subject: [PATCH] Free previously used tables [Bug #18134] --- hash.c | 22 +++++++--------------- test/ruby/test_hash.rb | 9 +++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/hash.c b/hash.c index 6675b81e65..314ac18c5c 100644 --- a/hash.c +++ b/hash.c @@ -2951,25 +2951,17 @@ rb_hash_replace(VALUE hash, VALUE hash2) COPY_DEFAULT(hash, hash2); if (RHASH_AR_TABLE_P(hash)) { - if (RHASH_AR_TABLE_P(hash2)) { - ar_clear(hash); - } - else { - ar_free_and_clear_table(hash); - RHASH_ST_TABLE_SET(hash, st_init_table_with_size(RHASH_TYPE(hash2), RHASH_SIZE(hash2))); - } + ar_free_and_clear_table(hash); } else { - if (RHASH_AR_TABLE_P(hash2)) { - st_free_table(RHASH_ST_TABLE(hash)); - RHASH_ST_CLEAR(hash); - } - else { - st_clear(RHASH_ST_TABLE(hash)); - RHASH_TBL_RAW(hash)->type = RHASH_ST_TABLE(hash2)->type; - } + st_free_table(RHASH_ST_TABLE(hash)); + RHASH_ST_CLEAR(hash); } hash_copy(hash, hash2); + if (RHASH_EMPTY_P(hash2) && RHASH_ST_TABLE_P(hash2)) { + /* ident hash */ + RHASH_ST_TABLE_SET(hash, st_init_table_with_size(RHASH_TYPE(hash2), 0)); + } rb_gc_writebarrier_remember(hash); diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index ac0d1dfce3..f79879c20a 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1278,6 +1278,15 @@ class TestHash < Test::Unit::TestCase assert_raise(FrozenError) { h2.replace(42) } end + def test_replace_memory_leak + assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}") + h = ("aa".."zz").each_with_index.to_h + 10_000.times {h.dup} + begin; + 500_000.times {h.dup.replace(h)} + end; + end + def test_size2 assert_equal(0, @cls[].size) end