Fix memory leak in rb_reg_search_set_match

https://github.com/ruby/ruby/pull/12801 changed regexp matches to reuse
the backref, which causes memory to leak if the original registers of the
match is not freed.

For example, the following script leaks memory:

    10.times do
      1_000_000.times do
        "aaaaaaaaaaa".gsub(/a/, "")
      end

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

Before:

    774256
    1535152
    2297360
    3059280
    3821296
    4583552
    5160304
    5091456
    5114256
    4980192

After:

    12480
    11440
    11696
    11632
    11632
    11760
    11824
    11824
    11824
    11888
This commit is contained in:
Peter Zhu 2025-03-11 15:05:05 -04:00
parent 1b2cc9c2b8
commit 1cdec3240b
Notes: git 2025-03-12 01:55:22 +00:00
2 changed files with 15 additions and 0 deletions

3
re.c
View file

@ -1829,6 +1829,9 @@ rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_back
if (NIL_P(match)) {
match = match_alloc(rb_cMatch);
}
else {
onig_region_free(&RMATCH_EXT(match)->regs, false);
}
rb_matchext_t *rm = RMATCH_EXT(match);
rm->regs = regs;

View file

@ -999,6 +999,18 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('foobazquux/foobazquux', result, bug8856)
end
def test_regsub_no_memory_leak
assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~"end;"}", rss: true)
code = proc do
"aaaaaaaaaaa".gsub(/a/, "")
end
1_000.times(&code)
begin;
100_000.times(&code)
end;
end
def test_ignorecase
v = assert_deprecated_warning(/variable \$= is no longer effective/) { $= }
assert_equal(false, v)