Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Methods of Memory Management in MRI
Search
Aaron Patterson
November 16, 2016
Technology
3
840
Methods of Memory Management in MRI
This is my RubyConf 2016 talk about Ruby's GC
Aaron Patterson
November 16, 2016
Tweet
Share
More Decks by Aaron Patterson
See All by Aaron Patterson
Speeding up Instance Variables in Ruby 3.3
tenderlove
2
370
[Feature #20425] Speeding up delegate methods
tenderlove
3
230
RailsConf 2023
tenderlove
29
1k
Don't @ Me! Faster Instance Variables with Object Shapes
tenderlove
1
420
RailsConf 2022 Keynote
tenderlove
2
520
Some Assembly Required
tenderlove
1
530
HexDevs 2021
tenderlove
1
400
Compacting GC for MRI
tenderlove
60
4.5k
But At What Cost?
tenderlove
9
15k
Other Decks in Technology
See All in Technology
Postman AI Agent Builderで AI Agentic workflow のプロトタイピング / Prototyping AI Agentic Workflow with Postman AI Agent Builder
yokawasa
0
200
AI自体のOps 〜LLMアプリの運用、AWSサービスとOSSの使い分け〜
minorun365
PRO
10
1.4k
20250307_エンジニアじゃないけどAzureはじめてみた
ponponmikankan
2
280
開発者のための FinOps/FinOps for Engineers
oracle4engineer
PRO
2
300
エンジニア主導の企画立案を可能にする組織とは?
recruitengineers
PRO
1
360
事業を差別化する技術を生み出す技術
pyama86
4
1.2k
AWSアカウントのセキュリティ自動化、どこまで進める? 最適な設計と実践ポイント
yuobayashi
7
2.1k
社内でKaggle部を作って初学者育成した話
daikon99
1
210
開発者体験を定量的に把握する手法と活用事例
ham0215
0
160
20250304_赤煉瓦倉庫_DeepSeek_Deep_Dive
hiouchiy
2
150
マーケットプレイス版Oracle WebCenter Content For OCI
oracle4engineer
PRO
3
560
[OpsJAWS Meetup33 AIOps] Amazon Bedrockガードレールで守る安全なAI運用
akiratameto
1
150
Featured
See All Featured
Navigating Team Friction
lara
183
15k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7.1k
Writing Fast Ruby
sferik
628
61k
The World Runs on Bad Software
bkeepers
PRO
67
11k
Side Projects
sachag
452
42k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
366
25k
Docker and Python
trallard
44
3.3k
Fontdeck: Realign not Redesign
paulrobertlloyd
83
5.4k
Why Our Code Smells
bkeepers
PRO
336
57k
A Modern Web Designer's Workflow
chriscoyier
693
190k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7.1k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.6k
Transcript
Interactive Portion
Unlock your phones
Selfie Sticks
Two Purposes
Help you identify people who like fun stuff and to
have a fun time
Let you know who doesn’t like to have fun or
like fun things
Have you seen this?
None
It’s me!
None
None
None
None
None
None
Methods of Memory Management in MRI
mmmmRuby
Aaron Patterson @tenderlove PGP Fingerprint: 4CE9 1B75 A798 28E8 6B1A
A8BB 9531 70BC B4FF AFC6
None
I am from Seattle
Which is not Ohio
!
"Ohio"
h GitHub
LeGit
GitHub Certified® Engineer
Bear Metal
x tenderlove
Please call me "Aaron"
("tenderlove" is fine too)
I love cats!
Cats are the best!
None
None
None
None
None
I have stickers of my cats
I also have GitHub stickers!
I was in the news recently
None
Keyboards
None
None
New Ruby Features
Soft Typing
Dynamic Typing
Static Typing
GC
Garbage Collector
Memory Terms
Stack Heap
Heap Ruby Heap
GC in MRI
Apps in Production
Scaling Issues
Tuning Issues
What I want you to learn
If you don’t know much about GC
If you know about GC terminology
If you already know the algorithms
If you already know the new stuff
GC Algorithms (in MRI)
Two sides of a GC
Collection Algorithm
Allocation Algorithm
Introspection API
Tuning Variables
Collection Algorithm
None
None
What type is the MRI collector?
Generational Incremental Mark & Sweep
High level: What is a GC?
Tree of Objects Root A B C D a =
[ { c: 'd' } ] Ruby Array Hash String Symbol
Tree of Objects Root A B C D a =
[ { c: 'd' } ] a = nil Ruby Array Hash String Symbol
Important Words!
Root Set
Garbage
Live Data
GC’s job: Find unlinked nodes, then free them
How to find unlinked nodes
Mark & Sweep
2 distinct phases
Mark Phase Root D A B C F E H
G
Mark Phase Root D A B C F E H
G
Mark Phase Root D A B C F E H
G
Mark Phase Root D A B C F E H
G
Sweep Phase Root D A B C F E H
G
Sweep Phase Root D A B C F
Mark & Sweep Very Easy! Too Slow
"Stop the world"
Visits every object, every time
Walk every object every time
Generational
Objects die young
Divide objects in to "old" and "new"
Generational Root A B D C Gen 0 Gen 1
Generational Root A B D C Gen 0 Gen 1
Generational Root A B D C Gen 0 Gen 1
Generational Root B D Gen 0 Gen 1
Generational Root B D Gen 0 Gen 1 E F
G
Generational Root B D Gen 0 Gen 1 E F
G
Generational Root B D Gen 0 Gen 1 E F
G
Generational Root B D Gen 0 Gen 1 F G
Generational Root B D Gen 0 Gen 1 F G
We didn’t have to touch "B"
One Slight Problem
Generational Root B D Gen 0 Gen 1
Generational Root B D Gen 0 Gen 1 E U
nused!
Remembered Set Root B D Gen 0 Gen 1 E
Remembered Set Root B D Gen 0 Gen 1 E
Remember!
Remembered Set Root B D Gen 0 Gen 1 E
Remembered Set Root B D Gen 0 Gen 1 E
Remembered Set Root B D Gen 0 Gen 1 E
Important Words
Write Barrier
Remembered Set
Generational Fast(er)! Not so easy
"Stop the world"
Incremental GC
Tri-Color Marking
Object Colors • White: will be collected • Black: No
reference to white objects, but reachable from the root • Gray: Reachable from root, but not yet scanned
Algorithm 1. Pick an object from the gray set and
move it to the black set 2. For each object it references, move it to the gray set 3. Repeat 1 and 2 until the gray set is empty
Tri-Color Marking Root H G F C B D A
E
Tri-Color Marking Root H G F C B D A
E
Tri-Color Marking Root H G F C B D A
E
Tri-Color Marking Root H G F C B D A
E
Tri-Color Marking Root H G F C B D A
E
None
What is the benefit?
We can interrupt steps!
Each step can be performed incrementally
Halting time is reduced
One Slight Problem
Tri-Color Marking Root F C B D A
Tri-Color Marking Root F C B D A G
Tri-Color Marking Root F C B D A G
Tri-Color Marking Root F C B D A G Write!
Important Words
Incremental
Write Barrier
Remembered Set
Minimize tracing
Decrease halting
Things our GC is not
Parallel
Real-Time
Compacting
Allocation Algorithm
None
None
Heap layout
malloc isn’t free GET IT?????
Large Chunk: Page (or Slab)
Page memory is contiguous
Each page holds a linked list
Nodes are called "slots"
Each slot is a Ruby object
Page Layout Page Ruby Object Ruby Object Ruby Object Ruby
Object Ruby Object
"Free List"
Find the first open slot!
Move to the next space in the free list
"Bump Pointer" allocation
Full Pages Object Object Object Object Page Page
"Eden" pages are searched
GC Time Object Object Object Object ☠
Important Words
Slot
Page
Eden
Tomb
Interesting Allocation Hacks
Not every object requires allocation
1 Page: 16k
1 Object: 40 bytes
Pages are "aligned"
Not `malloc` but "aligned malloc" `posix_memalign`
Choose "40" as a multiple
Start = 40 >> start = 40 => 40 >>
(start * 1).to_s(2).rjust(10, '0') => "0000101000" >> (start * 2).to_s(2).rjust(10, '0') => "0001010000" >> (start * 3).to_s(2).rjust(10, '0') => "0001111000" >> (start * 4).to_s(2).rjust(10, '0') => "0010100000" >> (start * 5).to_s(2).rjust(10, '0') => "0011001000" >> (start * 6).to_s(2).rjust(10, '0') => "0011110000" >> (start * 7).to_s(2).rjust(10, '0') => "0100011000"
"0000101000" "0001010000" "0001111000" "0010100000" "0011001000" "0011110000" "0100011000"
Use these bits to add meaning
Represent Integers Without Allocation
Encode Number 2 >> INT_FLAG = 0x1 => 1 >>
2.to_s(2) => "10" >> ((2 << 1) | INT_FLAG).to_s(2) => "101"
Decode Number 2 >> 0b101 => 5 >> 0b101 >>
1 => 2
Biggest Fixnum >> ((2 ** 64) - 1).to_s(2) => "11111111111111111111111111111111111111111111111
11111111111111111" >> ((2 ** 64) - 1).class => Bignum >> ((2 ** 63) - 1).class => Bignum >> ((2 ** 62) - 1).class => Fixnum >> ((2 ** 62)).class => Bignum
Biggest Before Heap Allocation >> ((2 ** 64) - 1).to_s(2)
=> "11111111111111111111111111111111111111111111111 11111111111111111" >> ((2 ** 64) - 1).class => Integer >> ((2 ** 63) - 1).class => Integer >> ((2 ** 62) - 1).class => Integer >> ((2 ** 62)).class => Integer R uby 2.4
Fixnums are singletons >> ((2 ** 62) - 1).object_id =>
9223372036854775807 >> ((2 ** 62) - 1).object_id => 9223372036854775807 >> ((2 ** 62)).object_id => 70213216135940 >> ((2 ** 62)).object_id => 70213216117840
Tagged Pointer
Tagged Pointers • Fixnum • Floats • True / False
/ Nil • Symbols
Allocation Problems
Poor Reclamation Object Object Object Object Object Object Object Object
Object Object Object Object
Poor Reclamation Object Object Object Object Object Object Object Object
NOPE
Poor Reclamation Object Object Object Object Object Object Object Object
CoW problems
1 Ruby Memory Page != 1 OS Memory Page
1 Ruby Page: 16k 1 OS Page: 4k
Object Object Object Parent Process Child Process Object
OS Copies 1 OS Page
We wrote 40 bytes, but 4kb got copied.
Solution: Group Old Objects
Two Page Types Probably Old Page Page Object Object
What is going to be old?
Probably Old class Foo end module Bar CONSTANT = Object.new
def foo "frozen string".freeze end end
Statistically Determined
Efficiently use space
Reduce GC time
CoW Friendly
GC Work G GitHub
None
None
Key Objects Class / Module Empty
None
None
None
None
None
None
~17% Smaller Heap
None
github.com/ github/ruby
Future Work
Moving Objects
Poor Reclamation Object Object Object Object Object Object Object Object
NOPE YEP
Stack Scanning, Forward Pointers
GC Introspection
Seeing GC info
GC.stat {:count=>21, :heap_allocated_pages=>87, :heap_sorted_length=>87, :heap_allocatable_pages=>0, :heap_available_slots=>35460, :heap_live_slots=>35342, :heap_free_slots=>118, :heap_final_slots=>0, :heap_marked_slots=>22207,
:heap_eden_pages=>87, :heap_tomb_pages=>0, :total_allocated_pages=>87, :total_freed_pages=>0, :total_allocated_objects=>208577, :total_freed_objects=>173235, :malloc_increase_bytes=>5152, :malloc_increase_bytes_limit=>16777216, :minor_gc_count=>19, :major_gc_count=>2, :remembered_wb_unprotected_objects=>189, :remembered_wb_unprotected_objects_limit=>374, :old_objects=>21977, :old_objects_limit=>39876, :oldmalloc_increase_bytes=>485600, :oldmalloc_increase_bytes_limit=>16777216}
GC Performance
GC::Profiler >> GC::Profiler.enable => nil >> GC::Profiler.report => nil >>
GC.start => nil >> GC::Profiler.report GC 22 invokes. Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms) 1 0.143 906920 1419840 35496 3.72500000000000586198 => nil >> GC.start => nil >> GC::Profiler.report GC 23 invokes. Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms) 1 0.143 906920 1419840 35496 3.72500000000000586198 2 0.148 906920 1419840 35496 3.47800000000000864020
GC::Profiler.enable
GC::Profiler.report
Heap Inspection
ObjectSpace.dump_all
ObjectSpace.dump >> require 'objspace' => false >> x = Object.new
=> #<Object:0x007fbcd09334a8> >> ObjectSpace.dump x => "{\"address\":\"0x007fbcd09334a8\", \"type\":\"OBJECT\", \"class\": \"0x007fbcd08dd878\", \"ivars\":0, \"memsize\":40, \"flags\": {\"wb_protected\":true}}\n"
ObjectSpace.dump >> x = Object.new => #<Object:0x007fbcd0959248> >> JSON.parse(ObjectSpace.dump(x))['flags'] =>
{"wb_protected"=>true} >> GC.start => nil >> JSON.parse(ObjectSpace.dump(x))['flags'] => {"wb_protected"=>true} >> GC.start => nil >> JSON.parse(ObjectSpace.dump(x))['flags'] => {"wb_protected"=>true} >> GC.start => nil >> JSON.parse(ObjectSpace.dump(x))['flags'] => {"wb_protected"=>true, "old"=>true, "uncollectible"=>true, "marked"=>true}
Object have 3 generations
ObjectSpace
trace_object_allocations
GC Tuning
RUBY_GC_HEAP _FREE_SLOTS Number of free slots available after a GC
RUBY_GC_HEAP _INIT_SLOTS Number of free slots to initialize the GC
with
RUBY_GC_HEAP_GR OWTH_MAX_SLOTS Never grow more than this many objects
THANKS!
None