Skip to content

Fix memory leak in GC when using Ractors #4613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 15, 2021

Conversation

peterzhu2118
Copy link
Member

Redmine ticket: https://bugs.ruby-lang.org/issues/18014

When a Ractor is removed, the freelist in the Ractor cache is not returned to the GC, leaving the freelist permanently lost.

The following script demonstrates the issue. It iterates 2000 times, in each iteration it simply creates a new Ractor that creates a new object. It stores the objects in an array in the main thread to prevent the object from being GC'd. This is important as if the object is GC'd the heap page will be freed if the whole page is empty.

arr = []

2000.times do
  # Start new Ractor that creates a new object
  arr << Ractor.new { Object.new }.take
  puts GC.stat(:heap_allocated_pages)
end

We can now graph the output from master and the branch with the patch.

We can see that the Ractor implementation creates heap pages linearly with the number of iterations. On my machine, this script on master uses about 43MB of memory while the patched version uses 13MB.

This is because when a new heap page is created (with 409 empty slots), the Ractor uses the page to allocate a single object, and leaks the remaining slots (408 slots).

Patch

The patch recycles the freelist when the Ractor is destroyed, preventing a memory leak from occurring.

An assertion has been added after gc_page_sweep to verify that the freelist length is equal to the number of free slots in the page.

@peterzhu2118 peterzhu2118 requested a review from ko1 June 29, 2021 19:50
@peterzhu2118 peterzhu2118 changed the title Memory leak in GC when using Ractors Fix memory leak in GC when using Ractors Jun 29, 2021
@peterzhu2118 peterzhu2118 force-pushed the pz-ractor-mem-leak branch 2 times, most recently from 6309292 to 294580c Compare June 30, 2021 13:29
If we force recycle an object before the page is swept, we should clear
it in the mark bitmap. If we don't clear it in the bitmap, then during
sweeping we won't account for this free slot so the `free_slots` count
of the page will be incorrect.
When a Ractor is removed, the freelist in the Ractor cache is not
returned to the GC, leaving the freelist permanently lost. This commit
recycles the freelist when the Ractor is destroyed, preventing a memory
leak from occurring.
This commit adds an assertion has been added after `gc_page_sweep` to
verify that the freelist length is equal to the number of free slots in
the page.
Copy link
Member

@tenderlove tenderlove left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a good change to me. The refactor is pretty straight forward.

@peterzhu2118 peterzhu2118 merged commit e5fe486 into ruby:master Jul 15, 2021
@peterzhu2118 peterzhu2118 deleted the pz-ractor-mem-leak branch July 15, 2021 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants