Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Memory leak when duplicating identhash
[Bug #20145]

Before this commit, both copy_compare_by_id and hash_copy will create a
copy of the ST table, so the ST table created in copy_compare_by_id will
be leaked.

    h = { 1 => 2 }.compare_by_identity

    10.times do
      1_000_000.times do
        h.select { false }
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    110736
    204352
    300272
    395520
    460704
    476736
    542000
    604704
    682624
    770528

After:

    15504
    16048
    16144
    16256
    16320
    16320
    16752
    16752
    16752
    16752
  • Loading branch information
peterzhu2118 authored and djensenius committed Jan 31, 2024
commit c27bc9c6a00eb083eb9da79315ef596e8e15679f
10 changes: 9 additions & 1 deletion hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,15 @@ hash_copy(VALUE ret, VALUE hash)
static VALUE
hash_dup_with_compare_by_id(VALUE hash)
{
return hash_copy(copy_compare_by_id(rb_hash_new(), hash), hash);
VALUE dup = hash_alloc_flags(rb_cHash, 0, Qnil, RHASH_ST_TABLE_P(hash));
if (RHASH_ST_TABLE_P(hash)) {
RHASH_SET_ST_FLAG(dup);
}
else {
RHASH_UNSET_ST_FLAG(dup);
}

return hash_copy(dup, hash);
}

static VALUE
Expand Down
10 changes: 10 additions & 0 deletions test/ruby/test_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,16 @@ def test_compare_by_identity
assert_predicate(h.dup, :compare_by_identity?, bug8703)
end

def test_compare_by_identy_memory_leak
assert_no_memory_leak([], "", "#{<<~"begin;"}\n#{<<~'end;'}", "[Bug #20145]", rss: true)
begin;
h = { 1 => 2 }.compare_by_identity
1_000_000.times do
h.select { false }
end
end;
end

def test_same_key
bug9646 = '[ruby-dev:48047] [Bug #9646] Infinite loop at Hash#each'
h = @cls[a=[], 1]
Expand Down