summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2021-08-29 16:47:26 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2021-08-29 17:18:58 +0900
commita615885f1e87f4bfbc5398b060fd3a64d5de8c4a (patch)
treec0045c949a33c1e661744e8d8e9f70deb1d36629
parent265a725830a487b846bc32f70346f6438c95a7e9 (diff)
Free previously used tables [Bug #18134]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/4788
-rw-r--r--hash.c22
-rw-r--r--test/ruby/test_hash.rb9
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