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

Reduce Ruby Memory Usage With These 12 Weird Tricks

Reduce Ruby Memory Usage With These 12 Weird Tricks

Nate Berkopec

November 11, 2016
Tweet

More Decks by Nate Berkopec

Other Decks in Programming

Transcript

  1. Halve Your Memory Usage With These 12 Weird Tricks Heroku

    and AWS hate him! @nateberkopec
  2. None
  3. None
  4. None
  5. Ruby is a garbage collected language. But my memory is

    growing. Therefore, memory leak.
  6. None
  7. Solution 1: Dial Back The Instance Counts Discover true steady-state

    memory usage per instance
  8. None
  9. Myth: Memory usage should look like a long, flat line

  10. None
  11. None
  12. None
  13. Aim for 300MB per instance This also applies to Sidekiq

  14. Solution 2: Stop allocating so many objects at once

  15. Myth: "Shouldn't GC clean the unused objects up after the

    job completes?"
  16. Translated: Memory goes down, right?

  17. None
  18. Thresholds Heap frag free isn't free

  19. Thresholds, not timers.

  20. Slots run out oldmalloc malloc

  21. Heap fragmentation

  22. None
  23. None
  24. None
  25. GC::INTERNAL_CONSTANTS { :RVALUE_SIZE=>40, :HEAP_PAGE_OBJ_LIMIT=>408, :HEAP_PAGE_BITMAP_SIZE=>56, :HEAP_PAGE_BITMAP_PLANES=>4 }

  26. Malloc and free are suggestions, not commands

  27. None
  28. None
  29. Heap fragmentation can cause long-term slow "leaks"

  30. None
  31. End result: Ruby Memory usage = Maximum Memory Pressure

  32. None
  33. Allocating less Or, fix your god damn N+1s

  34. Solution 2a: Use an APM - Scout, Skylight, New Relic

  35. Solution 2b: Use memory_profiler or oink

  36. None
  37. oink -- SUMMARY -- Worst Requests: 1. Feb 02 16:26:06,

    157524 KB, SportsController#show 2. Feb 02 20:11:54, 134972 KB, DashboardsController#show 3. Feb 02 19:06:13, 131912 KB, DashboardsController#show 4. Feb 02 08:07:46, 115448 KB, GroupsController#show 5. Feb 02 12:19:53, 112924 KB, GroupsController#show 6. Feb 02 13:03:00, 112064 KB, ColorSchemesController#show 7. Feb 02 13:01:59, 109148 KB, SessionsController#create 8. Feb 02 06:11:17, 108456 KB, PublicPagesController#join 9. Feb 02 08:43:06, 94468 KB, CommentsController#create 10. Feb 02 20:49:44, 82340 KB, DashboardsController#show
  38. memory_profiler allocated memory by gem ----------------------------------- rubygems x 305879 allocated

    memory by file ----------------------------------- /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb x 285433 /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems/basic_specification.rb x 18597 /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems.rb x 2218 /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems/specification.rb x 1169 /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems/defaults.rb x 520 /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb x 80 /home/sam/.rbenv/versions/2.1.0-github/lib/ruby/2.1.0/rubygems/version.rb x 80
  39. Make your own: objectspace & gc.stat

  40. GC.stat - log it! { :count => 9, :heap_allocated_pages =>

    74, :heap_sorted_length => 75, :heap_allocatable_pages => 0, :heap_available_slots => 30164, :heap_live_slots => 29863, :heap_free_slots => 301, :heap_final_slots => 0, # etc etc }
  41. ObjectSpace.count_objects { :TOTAL => 30164, :FREE => 235, :T_OBJECT =>

    297, :T_CLASS => 944, :T_MODULE => 45, :T_FLOAT => 4, # etc etc }
  42. Solution 2c: If all else fails, move to Rake tasks

    Throwaway VMs are better than bloated VMs
  43. None
  44. Solution 3: Gemfile audit with derailed

  45. $ bundle exec derailed bundle:mem TOP: 54.1836 MiB mail: 18.9688

    MiB mime/types: 17.4453 MiB mail/field: 0.4023 MiB mail/message: 0.3906 MiB action_view/view_paths: 0.4453 MiB action_view/base: 0.4336 MiB
  46. Myth: Dependencies are free!

  47. require false for assets! gem 'sass', require: false

  48. sprockets/lib/sprockets/ autoload.rb module Sprockets module Autoload autoload :Babel, 'sprockets/autoload/babel' autoload

    :Closure, 'sprockets/autoload/closure' autoload :CoffeeScript, 'sprockets/autoload/coffee_script' autoload :Eco, 'sprockets/autoload/eco' #etc etc etc
  49. Solution 4: jemalloc

  50. “emphasizes fragmentation avoidance and scalable concurrency support.”

  51. LD_PRELOAD or --with-jemalloc

  52. Solution 5: Use copy-on-write Puma, Unicorn or Passenger w/preloading

  53. Copy-on-write increases shared memory

  54. None
  55. Myth: Memory usage = sum of RSS Memory is surprisingly

    difficult to measure
  56. memory can be virtual/real shared/private resident/swapped

  57. It isn't perfect but it's a start

  58. Solution 6: Use a threaded webserver Puma, Passenger Enterprise. Increase

    concurrency w/lighter methods
  59. Mini-Myth: My application isn't thread-safe

  60. None
  61. minitest/hell

  62. Solution 7: Keep Ruby and gems up-to-date

  63. Ruby 2.2+ Rails 4.2+ Watch out for Ruby 2.4

  64. Solution 8: Tune malloc

  65. MALLOC_ARENA_MAX

  66. mallopt

  67. Solution 9: Tune GC

  68. If you can't read gc.c and understand these variables yourself,

    don't touch them (yet)
  69. GC Tuning can fix: Too many free slots Slow startup

    Too many or too few GCs
  70. Performance Birds of Feather Tomorrow @ 1:15pm

  71. 350+ pages, 18+ hours of video railsspeed.com

  72. None
  73. 10kb speedshop.co

  74. Thanks! Slides and notes on Twitter @nateberkopec speedshop.co & railsspeed.com

    Tell me your problems!