Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Methods of Memory Management in MRI

Methods of Memory Management in MRI

This is my RubyConf 2016 talk about Ruby's GC

F29327647a9cff5c69618bae420792ea?s=128

Aaron Patterson

November 16, 2016
Tweet

Transcript

  1. Interactive Portion

  2. Unlock your phones

  3. Selfie Sticks

  4. Two Purposes

  5. Help you identify people who like fun stuff and to

    have a fun time
  6. Let you know who doesn’t like to have fun or

    like fun things
  7. Have you seen this?

  8. None
  9. It’s me!

  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. Methods of Memory Management in MRI

  17. mmmmRuby

  18. Aaron Patterson @tenderlove PGP Fingerprint: 4CE9 1B75 A798 28E8 6B1A

    A8BB 9531 70BC B4FF AFC6
  19. None
  20. I am from Seattle

  21. Which is not Ohio

  22. !

  23. "Ohio"

  24. h GitHub

  25. LeGit

  26. GitHub Certified® Engineer

  27. Bear Metal

  28. x tenderlove

  29. Please call me "Aaron"

  30. ("tenderlove" is fine too)

  31. I love cats!

  32. Cats are the best!

  33. None
  34. None
  35. None
  36. None
  37. None
  38. I have stickers of my cats

  39. I also have GitHub stickers!

  40. I was in the news recently

  41. None
  42. Keyboards

  43. None
  44. None
  45. New Ruby Features

  46. Soft Typing

  47. Dynamic Typing

  48. Static Typing

  49. GC

  50. Garbage Collector

  51. Memory Terms

  52. Stack Heap

  53. Heap Ruby Heap

  54. GC in MRI

  55. Apps in Production

  56. Scaling Issues

  57. Tuning Issues

  58. What I want you to learn

  59. If you don’t know much about GC

  60. If you know about GC terminology

  61. If you already know the algorithms

  62. If you already know the new stuff

  63. GC Algorithms (in MRI)

  64. Two sides of a GC

  65. Collection Algorithm

  66. Allocation Algorithm

  67. Introspection API

  68. Tuning Variables

  69. Collection Algorithm

  70. None
  71. None
  72. What type is the MRI collector?

  73. Generational Incremental Mark & Sweep

  74. High level: What is a GC?

  75. Tree of Objects Root A B C D a =

    [ { c: 'd' } ] Ruby Array Hash String Symbol
  76. Tree of Objects Root A B C D a =

    [ { c: 'd' } ] a = nil Ruby Array Hash String Symbol
  77. Important Words!

  78. Root Set

  79. Garbage

  80. Live Data

  81. GC’s job: Find unlinked nodes, then free them

  82. How to find unlinked nodes

  83. Mark & Sweep

  84. 2 distinct phases

  85. Mark Phase Root D A B C F E H

    G
  86. Mark Phase Root D A B C F E H

    G
  87. Mark Phase Root D A B C F E H

    G
  88. Mark Phase Root D A B C F E H

    G
  89. Sweep Phase Root D A B C F E H

    G
  90. Sweep Phase Root D A B C F

  91. Mark & Sweep Very Easy! Too Slow

  92. "Stop the world"

  93. Visits every object, every time

  94. Walk every object every time

  95. Generational

  96. Objects die young

  97. Divide objects in to "old" and "new"

  98. Generational Root A B D C Gen 0 Gen 1

  99. Generational Root A B D C Gen 0 Gen 1

  100. Generational Root A B D C Gen 0 Gen 1

  101. Generational Root B D Gen 0 Gen 1

  102. Generational Root B D Gen 0 Gen 1 E F

    G
  103. Generational Root B D Gen 0 Gen 1 E F

    G
  104. Generational Root B D Gen 0 Gen 1 E F

    G
  105. Generational Root B D Gen 0 Gen 1 F G

  106. Generational Root B D Gen 0 Gen 1 F G

  107. We didn’t have to touch "B"

  108. One Slight Problem

  109. Generational Root B D Gen 0 Gen 1

  110. Generational Root B D Gen 0 Gen 1 E U

    nused!
  111. Remembered Set Root B D Gen 0 Gen 1 E

  112. Remembered Set Root B D Gen 0 Gen 1 E

    Remember!
  113. Remembered Set Root B D Gen 0 Gen 1 E

  114. Remembered Set Root B D Gen 0 Gen 1 E

  115. Remembered Set Root B D Gen 0 Gen 1 E

  116. Important Words

  117. Write Barrier

  118. Remembered Set

  119. Generational Fast(er)! Not so easy

  120. "Stop the world"

  121. Incremental GC

  122. Tri-Color Marking

  123. 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
  124. 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
  125. Tri-Color Marking Root H G F C B D A

    E
  126. Tri-Color Marking Root H G F C B D A

    E
  127. Tri-Color Marking Root H G F C B D A

    E
  128. Tri-Color Marking Root H G F C B D A

    E
  129. Tri-Color Marking Root H G F C B D A

    E
  130. None
  131. What is the benefit?

  132. We can interrupt steps!

  133. Each step can be performed incrementally

  134. Halting time is reduced

  135. One Slight Problem

  136. Tri-Color Marking Root F C B D A

  137. Tri-Color Marking Root F C B D A G

  138. Tri-Color Marking Root F C B D A G

  139. Tri-Color Marking Root F C B D A G Write!

  140. Important Words

  141. Incremental

  142. Write Barrier

  143. Remembered Set

  144. Minimize tracing

  145. Decrease halting

  146. Things our GC is not

  147. Parallel

  148. Real-Time

  149. Compacting

  150. Allocation Algorithm

  151. None
  152. None
  153. Heap layout

  154. malloc isn’t free GET IT?????

  155. Large Chunk: Page (or Slab)

  156. Page memory is contiguous

  157. Each page holds a linked list

  158. Nodes are called "slots"

  159. Each slot is a Ruby object

  160. Page Layout Page Ruby Object Ruby Object Ruby Object Ruby

    Object Ruby Object
  161. "Free List"

  162. Find the first open slot!

  163. Move to the next space in the free list

  164. "Bump Pointer" allocation

  165. Full Pages Object Object Object Object Page Page

  166. "Eden" pages are searched

  167. GC Time Object Object Object Object ☠

  168. Important Words

  169. Slot

  170. Page

  171. Eden

  172. Tomb

  173. Interesting Allocation Hacks

  174. Not every object requires allocation

  175. 1 Page: 16k

  176. 1 Object: 40 bytes

  177. Pages are "aligned"

  178. Not `malloc` but "aligned malloc" `posix_memalign`

  179. Choose "40" as a multiple

  180. 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"
  181. "0000101000" "0001010000" "0001111000" "0010100000" "0011001000" "0011110000" "0100011000"

  182. Use these bits to add meaning

  183. Represent Integers Without Allocation

  184. Encode Number 2 >> INT_FLAG = 0x1 => 1 >>

    2.to_s(2) => "10" >> ((2 << 1) | INT_FLAG).to_s(2) => "101"
  185. Decode Number 2 >> 0b101 => 5 >> 0b101 >>

    1 => 2
  186. 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
  187. 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
  188. 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
  189. Tagged Pointer

  190. Tagged Pointers • Fixnum • Floats • True / False

    / Nil • Symbols
  191. Allocation Problems

  192. Poor Reclamation Object Object Object Object Object Object Object Object

    Object Object Object Object
  193. Poor Reclamation Object Object Object Object Object Object Object Object

    NOPE
  194. Poor Reclamation Object Object Object Object Object Object Object Object

  195. CoW problems

  196. 1 Ruby Memory Page != 1 OS Memory Page

  197. 1 Ruby Page: 16k 1 OS Page: 4k

  198. Object Object Object Parent Process Child Process Object

  199. OS Copies 1 OS Page

  200. We wrote 40 bytes, but 4kb got copied.

  201. Solution: Group Old Objects

  202. Two Page Types Probably Old Page Page Object Object

  203. What is going to be old?

  204. Probably Old class Foo end module Bar CONSTANT = Object.new

    def foo "frozen string".freeze end end
  205. Statistically Determined

  206. Efficiently use space

  207. Reduce GC time

  208. CoW Friendly

  209. GC Work G GitHub

  210. None
  211. None
  212. Key Objects Class / Module Empty

  213. None
  214. None
  215. None
  216. None
  217. None
  218. None
  219. ~17% Smaller Heap

  220. None
  221. github.com/ github/ruby

  222. Future Work

  223. Moving Objects

  224. Poor Reclamation Object Object Object Object Object Object Object Object

    NOPE YEP
  225. Stack Scanning, Forward Pointers

  226. GC Introspection

  227. Seeing GC info

  228. 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}
  229. GC Performance

  230. 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
  231. GC::Profiler.enable

  232. GC::Profiler.report

  233. Heap Inspection

  234. ObjectSpace.dump_all

  235. 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"
  236. 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}
  237. Object have 3 generations

  238. ObjectSpace

  239. trace_object_allocations

  240. GC Tuning

  241. RUBY_GC_HEAP _FREE_SLOTS Number of free slots available after a GC

  242. RUBY_GC_HEAP _INIT_SLOTS Number of free slots to initialize the GC

    with
  243. RUBY_GC_HEAP_GR OWTH_MAX_SLOTS Never grow more than this many objects

  244. THANKS!

  245. None