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

Memory Leaks, Tweaks, and Techniques

Memory Leaks, Tweaks, and Techniques

Richard Schneeman

November 07, 2015
Tweet

More Decks by Richard Schneeman

Other Decks in Programming

Transcript



  1. Memory
    Leaks
    2015
    @schneems

    View full-size slide

  2. Leaks are bad

    View full-size slide

  3. Obviously
    memory leaks
    are bad

    View full-size slide

  4. They
    Call me
    @Schneems

    View full-size slide

  5. “Favor
    composition
    over
    inheritance

    View full-size slide

  6. Golang
    rulz
    Elixr
    4 lyfe

    View full-size slide

  7. Rails
    Commit
    Bit

    View full-size slide

  8. I like
    working
    with
    wood

    View full-size slide

  9. Plan
    for
    seasonal
    wood
    movement

    View full-size slide

  10. Why is
    memory
    important?

    View full-size slide

  11. RAM is a
    limited
    resource

    View full-size slide

  12. When you run
    out, the OS
    starts using
    the HD

    View full-size slide

  13. When you run
    out of room…

    View full-size slide

  14. Performance
    Profiling

    View full-size slide

  15. Start with a
    known

    View full-size slide

  16. Repro the
    performance
    problem

    View full-size slide

  17. #include
    void f(void)
    {
    int* x = malloc(10 *sizeof(int));
    x[10] = 0;
    }
    int main(void) {
    f();
    return 0;
    }

    View full-size slide

  18. #include
    void f(void)
    {
    int* x = malloc(10 *sizeof(int));
    x[10] = 0;
    }
    int main(void) {
    f();
    return 0;
    }

    View full-size slide

  19. Find the right
    tools

    View full-size slide

  20. $ valgrind --tool=memcheck ./memoryleak
    ==31747== Memcheck, a memory error detector
    ==31747== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==31747== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
    ==31747== Command: ./memoryleak
    ==31747==
    ==31747== Invalid write of size 4
    ==31747== at 0x100000F4C: f (memoryleak.c:6)
    ==31747== by 0x100000F73: main (memoryleak.c:10)
    ==31747== Address 0x100ae34a8 is 0 bytes after a block of size 40 alloc'd
    ==31747== at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/
    valgrind/vgpreload_memcheck-amd64-darwin.so)
    ==31747== by 0x100000F43: f (memoryleak.c:5)
    ==31747== by 0x100000F73: main (memoryleak.c:10)
    ==31747==
    ==31747==
    ==31747== HEAP SUMMARY:
    ==31747== in use at exit: 35,304 bytes in 431 blocks
    ==31747== total heap usage: 503 allocs, 72 frees, 41,272 bytes allocated
    ==31747==
    ==31747== LEAK SUMMARY:
    ==31747== definitely lost: 40 bytes in 1 blocks
    ==31747== indirectly lost: 0 bytes in 0 blocks
    ==31747== possibly lost: 0 bytes in 0 blocks
    ==31747== still reachable: 0 bytes in 0 blocks
    ==31747== suppressed: 35,264 bytes in 430 blocks
    ==31747== Rerun with --leak-check=full to see details of leaked memory
    ==31747==
    ==31747== For counts of detected and suppressed errors, rerun with: -v
    ==31747== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    View full-size slide

  21. $ valgrind --tool=memcheck ./memoryleak
    ==31747== Memcheck, a memory error detector
    ==31747== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==31747== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
    ==31747== Command: ./memoryleak
    ==31747==
    ==31747== Invalid write of size 4
    ==31747== at 0x100000F4C: f (memoryleak.c:6)
    ==31747== by 0x100000F73: main (memoryleak.c:10)
    ==31747== Address 0x100ae34a8 is 0 bytes after a block of size 40 alloc'd
    ==31747== at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/
    valgrind/vgpreload_memcheck-amd64-darwin.so)
    ==31747== by 0x100000F43: f (memoryleak.c:5)
    ==31747== by 0x100000F73: main (memoryleak.c:10)
    ==31747==
    ==31747==
    ==31747== HEAP SUMMARY:
    ==31747== in use at exit: 35,304 bytes in 431 blocks
    ==31747== total heap usage: 503 allocs, 72 frees, 41,272 bytes allocated
    ==31747==
    ==31747== LEAK SUMMARY:
    ==31747== definitely lost: 40 bytes in 1 blocks
    ==31747== indirectly lost: 0 bytes in 0 blocks
    ==31747== possibly lost: 0 bytes in 0 blocks
    ==31747== still reachable: 0 bytes in 0 blocks
    ==31747== suppressed: 35,264 bytes in 430 blocks
    ==31747== Rerun with --leak-check=full to see details of leaked memory
    ==31747==
    ==31747== For counts of detected and suppressed errors, rerun with: -v
    ==31747== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    View full-size slide

  22. $ valgrind --tool=memcheck ./memoryleak
    ==31747== Memcheck, a memory error detector
    ==31747== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==31747== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
    ==31747== Command: ./memoryleak
    ==31747==
    ==31747== Invalid write of size 4
    ==31747== at 0x100000F4C: f (memoryleak.c:6)
    ==31747== by 0x100000F73: main (memoryleak.c:10)
    ==31747== Address 0x100ae34a8 is 0 bytes after a block of size 40 alloc'd
    ==31747== at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/
    valgrind/vgpreload_memcheck-amd64-darwin.so)
    ==31747== by 0x100000F43: f (memoryleak.c:5)
    ==31747== by 0x100000F73: main (memoryleak.c:10)
    ==31747==
    ==31747==
    ==31747== HEAP SUMMARY:
    ==31747== in use at exit: 35,304 bytes in 431 blocks
    ==31747== total heap usage: 503 allocs, 72 frees, 41,272 bytes allocated
    ==31747==
    ==31747== LEAK SUMMARY:
    ==31747== definitely lost: 40 bytes in 1 blocks
    ==31747== indirectly lost: 0 bytes in 0 blocks
    ==31747== possibly lost: 0 bytes in 0 blocks
    ==31747== still reachable: 0 bytes in 0 blocks
    ==31747== suppressed: 35,264 bytes in 430 blocks
    ==31747== Rerun with --leak-check=full to see details of leaked memory
    ==31747==
    ==31747== For counts of detected and suppressed errors, rerun with: -v
    ==31747== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    View full-size slide

  23. Apply the
    tools to your
    App

    View full-size slide

  24. How
    does
    Ruby
    Memory?

    View full-size slide

  25. Ruby is a
    “managed”
    Language

    View full-size slide

  26. Don’t manually
    allocate, use
    Garbage
    Collector
    instead

    View full-size slide

  27. 2 causes for
    increased
    memory use

    View full-size slide

  28. Retained
    Objects
    1)

    View full-size slide

  29. RETAINED = []
    1.upto(100_000_000).each do |i|
    RETAINED << “#{i}”
    end

    View full-size slide

  30. Memory
    Object slots

    View full-size slide

  31. Memory
    “1” “2” “3”“4” “5” “6” “7”
    RETAINED = []
    1.upto(100_000_000).each do |i|
    RETAINED << “#{i}”
    end

    View full-size slide

  32. Out of slots!
    Allocate more
    memory

    View full-size slide

  33. Memory
    “1” “2” “3”“4” “5” “6” “7”
    “10”
    “8” “9” “11” “12” “13” “14”

    View full-size slide

  34. Allocated
    Objects
    2)

    View full-size slide

  35. 1.upto(100_000_000).each do |i|
    puts "#{i}"
    end

    View full-size slide

  36. Memory
    “1” “2” “3”“4” “5” “6” “7”

    View full-size slide

  37. Out of slots!
    Run GC!

    View full-size slide

  38. Memory
    “2” “3”“4” “5” “6” “7”
    1.upto(100_000_000).each do |i|
    puts "#{i}"
    end
    “1”

    View full-size slide

  39. Memory
    “2” “3”“4” “5” “6” “7”
    1.upto(100_000_000).each do |i|
    puts "#{i}"
    end

    View full-size slide

  40. Memory
    “8” “3”“4” “5” “6” “7”
    1.upto(100_000_000).each do |i|
    puts "#{i}"
    end
    “2”

    View full-size slide

  41. GC will save
    us!

    View full-size slide

  42. def make_objects
    array = []
    1.upto(100_000_000).each do |i|
    array << "#{i}"
    end
    return nil
    end
    make_objects
    GC.start(full_mark: true,
    immediate_sweep: true)

    View full-size slide

  43. def make_objects
    array = []
    1.upto(100_000_000).each do |i|
    array << "#{i}"
    end
    return nil
    end
    make_objects
    GC.start(full_mark: true,
    immediate_sweep: true)

    View full-size slide

  44. def make_objects
    array = []
    1.upto(100_000_000).each do |i|
    array << "#{i}"
    end
    return nil
    end
    make_objects
    GC.start(full_mark: true,
    immediate_sweep: true)

    View full-size slide

  45. GC.start(full_mark: true, immediate_sweep: true)
    GC.disable
    # Code goes here
    before = GC.stat[:total_freed_objects]
    GC.enable
    GC.start(full_mark: true, immediate_sweep: true)
    after = GC.stat[:total_freed_objects]
    # => “Objects cleared by GC: 100_010_687”

    View full-size slide

  46. Cannot open
    slots while
    reference
    exists

    View full-size slide

  47. After
    program runs

    View full-size slide

  48. Memory
    “1” “2” “3”“4” “5” “6” “7”
    “10”
    “8” “9” “11” “12” “13” “14”

    View full-size slide

  49. Memory
    “1” “2” “3”“4” “5” “6” “7”
    “10”
    “8” “9” “11” “12” “13” “14”

    View full-size slide

  50. Ruby
    releases
    memory
    SLOWLY

    View full-size slide

  51. Test
    Everything
    (even these
    slides)

    View full-size slide

  52. How does this
    affect my
    Rails app?

    View full-size slide

  53. Retained
    Allocated
    App

    View full-size slide

  54. Request
    Starts

    View full-size slide

  55. Request Full
    .

    View full-size slide

  56. Request Full
    Full
    ..

    View full-size slide

  57. Request Full
    Full
    Full
    ...

    View full-size slide

  58. Request Full
    Full
    Full
    Full
    ....

    View full-size slide

  59. Request Full
    Full
    Full
    Full
    Full
    ......

    View full-size slide

  60. Out of
    memory
    Full
    Full
    Full
    Full
    Full

    View full-size slide

  61. Run GC,
    All objs
    needed
    0_o
    Full
    Full
    Full
    Full
    Full

    View full-size slide

  62. Allocate
    mem
    Full
    Full
    Full
    Full
    Full

    View full-size slide

  63. Request
    Done
    Full
    Full
    Full
    Full
    Full
    ........

    View full-size slide

  64. GC
    Objs
    not used
    Full
    Full
    Full
    Full
    Full

    View full-size slide

  65. GC
    Objs
    not used

    View full-size slide

  66. Memory
    increases even
    without
    object retention

    View full-size slide

  67. Object creation/
    deletion
    takes
    RAM

    View full-size slide

  68. Object creation/
    deletion
    takes
    Time

    View full-size slide

  69. Further
    Reading:

    View full-size slide

  70. Generational
    GC

    View full-size slide

  71. What is a
    memory leak?

    View full-size slide

  72. What is a
    memory leak?

    View full-size slide

  73. #include
    void f(void)
    {
    int* x = malloc(10 *sizeof(int));
    x[10] = 0;
    }
    int main(void) {
    f();
    return 0;
    }

    View full-size slide

  74. We lost the
    reference

    View full-size slide

  75. We can never
    free() that
    memory

    View full-size slide

  76. RETAINED = []
    1.upto(100_000_000).each do |i|
    RETAINED << “#{i}”
    end

    View full-size slide

  77. We retained
    the reference

    View full-size slide

  78. We can never
    GC that
    memory

    View full-size slide

  79. Apply the
    tools to your
    App

    View full-size slide

  80. Do you have a
    memory leak?

    View full-size slide

  81. Ruby memory
    should
    plateau

    View full-size slide

  82. Do you have a
    memory leak?

    View full-size slide

  83. Memory Bloat

    View full-size slide

  84. Real life is
    messy

    View full-size slide

  85. Easiest way
    to stop a
    memory bloat
    ???

    View full-size slide

  86. Puma
    Worker
    Killer

    View full-size slide

  87. Rolling
    Restarts

    View full-size slide

  88. Memory
    Triggered
    Restarts

    View full-size slide

  89. Doesn’t work
    with
    containers

    View full-size slide

  90. Puma
    Worker
    Killer

    View full-size slide

  91. Concurrency
    via
    processes

    View full-size slide

  92. Reduce
    “worker”
    count to
    reduce RAM

    View full-size slide

  93. Hardest way
    to stop
    memory bloat
    ???

    View full-size slide

  94. Debugging
    and
    Introspection

    View full-size slide

  95. Who has
    heard of
    derailed?

    View full-size slide

  96. derailed_benchmarks

    View full-size slide

  97. Memory
    at
    Boot

    View full-size slide

  98. $ 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

    View full-size slide

  99. Memory
    at
    Runtime

    View full-size slide

  100. $ bundle exec derailed exec perf:objects
    # ...
    ## allocated memory by location
    ---------------------------------------
    1935548 /Users/richardschneeman/.gem/ruby/2.2.3/gems/actionpack-4.2.3/lib/action_dispa
    896100 /Users/richardschneeman/.gem/ruby/2.2.3/gems/pg-0.16.0/lib/pg/result.rb:10
    741488 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerecord-4.2.3/lib/active_rec
    689299 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activesupport-4.2.3/lib/active_su
    660672 /Users/richardschneeman/.gem/ruby/2.2.3/gems/actionpack-4.2.3/lib/action_dispa
    606384 /Users/richardschneeman/Documents/projects/codetriage/app/views/repos/\_repo.h
    579384 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activesupport-4.2.3/lib/active\_s
    532800 /Users/richardschneeman/.gem/ruby/2.2.3/gems/actionpack-4.2.3/lib/action_dispa
    391392 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerecord-4.2.3/lib/active_rec
    385920 /Users/richardschneeman/.gem/ruby/2.2.3/gems/temple-0.7.5/lib/temple/utils.rb:

    View full-size slide

  101. $ heroku pg:pull

    View full-size slide

  102. $ PATH_TO_HIT=rails/rails bundle exec derailed exec perf:objects
    Booting: production
    Running 1 times
    Total allocated: 21495556 bytes (207735 objects)
    Total retained: 15041423 bytes (133220 objects)
    allocated memory by gem
    -----------------------------------
    19427636 activerecord-4.2.3
    1471380 activesupport-4.2.3
    214048 actionview-4.2.3
    151066 actionpack-4.2.3
    121983 codetriage/app
    24308 will_paginate-3.0.7
    23193 rack-1.6.4
    21105 arel-6.0.3
    13397 ruby-2.2.3/lib
    5560 warden-1.2.3
    5536 rack-timeout-0.3.2
    3912 activemodel-4.2.3
    3000 hashie-3.4.2
    2154 devise-3.5.2
    1573 omniauth-1.2.2
    1536 i18n-0.7.0
    1495 railties-4.2.3
    787 temple-0.7.6

    View full-size slide

  103. 19427636
    bytes…

    View full-size slide

  104. allocated memory by location
    -----------------------------------
    5131624 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    4616480 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    1336408 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    1336088 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    1330904 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    1325648 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    921807 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activesup
    737376 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    596288 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec
    460720 /Users/richardschneeman/.gem/ruby/2.2.3/gems/activerec

    View full-size slide

  105. allocated objects by class
    -----------------------------------
    108939 String
    35456 Hash
    21605 Array
    5890 Proc
    5886 RubyVM::Env
    5859 ActiveRecord::Attribute::FromDatabase
    5759 ActiveRecord::AttributeSet
    5759 ActiveRecord::LazyAttributeHash
    5731 ActiveRecord::Associations::BelongsToAssociation
    5731 Issue
    217 ActiveSupport::SafeBuffer
    173 MatchData
    112 ActionView::OutputBuffer
    59 Time
    43 ActiveRecord::Relation
    42 ActiveRecord::ConnectionAdapters::PostgreSQL::Name
    27 ActiveRecord::AssociationRelation
    27 User
    }

    View full-size slide

  106. allocated objects by class
    -----------------------------------
    108939 String
    35456 Hash
    21605 Array
    5890 Proc
    5886 RubyVM::Env
    5859 ActiveRecord::Attribute::FromDatabase
    5759 ActiveRecord::AttributeSet
    5759 ActiveRecord::LazyAttributeHash
    5731 ActiveRecord::Associations::BelongsToAssociation
    5731 Issue
    217 ActiveSupport::SafeBuffer
    173 MatchData
    112 ActionView::OutputBuffer
    59 Time
    43 ActiveRecord::Relation
    42 ActiveRecord::ConnectionAdapters::PostgreSQL::Name
    27 ActiveRecord::AssociationRelation
    27 User
    }

    View full-size slide

  107. allocated objects by class
    -----------------------------------
    108939 String
    35456 Hash
    21605 Array
    5890 Proc
    5886 RubyVM::Env
    5859 ActiveRecord::Attribute::FromDatabase
    5759 ActiveRecord::AttributeSet
    5759 ActiveRecord::LazyAttributeHash
    5731 ActiveRecord::Associations::BelongsToAssociation
    5731 Issue
    217 ActiveSupport::SafeBuffer
    173 MatchData
    112 ActionView::OutputBuffer
    59 Time
    43 ActiveRecord::Relation
    42 ActiveRecord::ConnectionAdapters::PostgreSQL::Name
    27 ActiveRecord::AssociationRelation
    27 User

    View full-size slide

  108. allocated objects by class
    -----------------------------------
    108939 String
    35456 Hash
    21605 Array
    5890 Proc
    5886 RubyVM::Env
    5859 ActiveRecord::Attribute::FromDatabase
    5759 ActiveRecord::AttributeSet
    5759 ActiveRecord::LazyAttributeHash
    5731 ActiveRecord::Associations::BelongsToAssociation
    5731 Issue
    217 ActiveSupport::SafeBuffer
    173 MatchData
    112 ActionView::OutputBuffer
    59 Time
    43 ActiveRecord::Relation
    42 ActiveRecord::ConnectionAdapters::PostgreSQL::Name
    27 ActiveRecord::AssociationRelation
    27 User

    View full-size slide

  109. NOT 5731 Issues
    }

    View full-size slide

  110. Started GET "/rails/rails" for ::1 at 2015-11-04 14:54:55 -0600
    ActiveRecord::SchemaMigration Load (19.2ms) SELECT "schema_migrations".* FROM "schema_migration
    Processing by ReposController#show as HTML
    Parameters: {"full_name"=>"rails/rails"}
    Repo Load (21.2ms) SELECT "repos".* FROM "repos" WHERE "repos"."full_name" = $1 LIMIT 1 [["fu
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=1006ms state=activ
    Issue Load (1091.1ms) SELECT "issues".* FROM "issues" WHERE "issues"."repo_id" IN (36)
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=2093ms state=activ
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=3101ms state=activ
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=4027ms state=activ
    (48.1ms) SELECT COUNT(*) FROM "users" INNER JOIN "repo_subscriptions" ON "users"."id" = "repo_
    User Load (53.4ms) SELECT "users".* FROM "users" INNER JOIN "repo_subscriptions" ON "users"."i
    Rendered subscribers/_avatars.html.slim (138.9ms)
    CACHE (0.0ms) SELECT COUNT(*) FROM "users" INNER JOIN "repo_subscriptions" ON "users"."id" = "r
    (0.6ms) SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "users" INNER JOIN "re
    36]]
    Issue Load (41.8ms) SELECT "issues".* FROM "issues" WHERE "issues"."repo_id" = $1 AND "issues"
    (44.2ms) SELECT COUNT(*) FROM "issues" WHERE "issues"."repo_id" = $1 AND "issues"."state" = $2
    Rendered repos/show.html.slim within layouts/application (496.5ms)
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=5005ms state=activ
    Rendered application/_head.html.slim (1360.4ms)
    Rendered application/_flashes.html.slim (15.9ms)
    Rendered application/_logo.html.slim (21.5ms)
    Rendered application/_nav.html.slim (73.0ms)
    Rendered application/_thoughtbot.html.slim (11.5ms)
    Rendered application/_footer.html.slim (36.4ms)
    Completed 200 OK in 5760ms (Views: 1889.1ms | ActiveRecord: 1323.2ms)
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=6004ms state=activ

    View full-size slide

  111. Started GET "/rails/rails" for ::1 at 2015-11-04 14:54:55 -0600
    ActiveRecord::SchemaMigration Load (19.2ms) SELECT "schema_migrations".* FROM "schema_migration
    Processing by ReposController#show as HTML
    Parameters: {"full_name"=>"rails/rails"}
    Repo Load (21.2ms) SELECT "repos".* FROM "repos" WHERE "repos"."full_name" = $1 LIMIT 1 [["fu
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=1006ms state=activ
    Issue Load (1091.1ms) SELECT "issues".* FROM "issues" WHERE "issues"."repo_id" IN (36)
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=2093ms state=activ
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=3101ms state=activ
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=4027ms state=activ
    (48.1ms) SELECT COUNT(*) FROM "users" INNER JOIN "repo_subscriptions" ON "users"."id" = "repo_
    User Load (53.4ms) SELECT "users".* FROM "users" INNER JOIN "repo_subscriptions" ON "users"."i
    Rendered subscribers/_avatars.html.slim (138.9ms)
    CACHE (0.0ms) SELECT COUNT(*) FROM "users" INNER JOIN "repo_subscriptions" ON "users"."id" = "r
    (0.6ms) SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "users" INNER JOIN "re
    36]]
    Issue Load (41.8ms) SELECT "issues".* FROM "issues" WHERE "issues"."repo_id" = $1 AND "issues"
    (44.2ms) SELECT COUNT(*) FROM "issues" WHERE "issues"."repo_id" = $1 AND "issues"."state" = $2
    Rendered repos/show.html.slim within layouts/application (496.5ms)
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=5005ms state=activ
    Rendered application/_head.html.slim (1360.4ms)
    Rendered application/_flashes.html.slim (15.9ms)
    Rendered application/_logo.html.slim (21.5ms)
    Rendered application/_nav.html.slim (73.0ms)
    Rendered application/_thoughtbot.html.slim (11.5ms)
    Rendered application/_footer.html.slim (36.4ms)
    Completed 200 OK in 5760ms (Views: 1889.1ms | ActiveRecord: 1323.2ms)
    source=rack-timeout id=22a6fea79c2f050a9d9999b007298d12 timeout=30000ms service=6004ms state=activ

    View full-size slide

  112. Loading EVERY
    issue for
    rails/rails
    repo

    View full-size slide

  113. def self.find_by_full_name(full_name)
    Repo.includes(:issues).find_by!(full_name: full_name)
    end

    View full-size slide

  114. def self.find_by_full_name(full_name)
    Repo.includes(:issues).find_by!(full_name: full_name)
    end

    View full-size slide

  115. Screenshot 2015-10-22 09.39.20

    View full-size slide

  116. Not a memory
    leak

    View full-size slide

  117. Objects were
    getting
    Collected via
    GC

    View full-size slide

  118. saltstack/salt

    View full-size slide

  119. rustlang/rust

    View full-size slide

  120. Was that
    necessary?

    View full-size slide

  121. /rails/rails
    Unused Eager Loading detected
    Repo => [:issues]
    Remove from your finder: :includes => [:issues]

    View full-size slide

  122. What if there
    was another
    cause?

    View full-size slide

  123. Proof that
    memory tooling
    provides
    results

    View full-size slide

  124. https://www.dropbox.com/s/vyd7teot0pg349q/Screenshot%202015-11-05%2011.34.04.png?dl=0

    View full-size slide

  125. $ bundle gem derailed_benchmarks

    View full-size slide

  126. mime-types
    boot

    View full-size slide

  127. https://www.dropbox.com/s/wdabatbn0crbgw2/Screenshot%202015-11-05%2011.54.12.png?dl=0

    View full-size slide

  128. Thanks
    @jeremyevans

    View full-size slide

  129. gem “mime-types”,
    “~> 2.6.1”,
    require: “mime/types/columnar”

    View full-size slide

  130. https://www.dropbox.com/s/56bw1mewpm7rc65/Screenshot%202015-11-05%2011.38.15.png?dl=0
    Omniauth
    28% faster

    View full-size slide

  131. https://www.dropbox.com/s/0n72bg81s0rsfdh/Screenshot%202015-11-05%2011.45.58.png?dl=0
    36% smaller
    require
    memory

    View full-size slide

  132. gem “mail”, “~> 2.6.2”

    View full-size slide

  133. https://www.dropbox.com/s/7j0m6zxl27yl7e7/Screenshot%202015-11-05%2011.47.07.png?dl=0
    10% faster
    requests in
    Rails

    View full-size slide

  134. 1+ years
    of building
    and using
    tools

    View full-size slide

  135. I made results
    however

    View full-size slide

  136. All these
    changes

    View full-size slide

  137. Now I can
    reproduce the a
    basic memory
    problem at will

    View full-size slide

  138. What did I
    learn from 1+
    years of PRs?

    View full-size slide

  139. Our community has
    almost
    zero
    knowledge of
    how our programs
    use memory

    View full-size slide

  140. Most apps are
    slow due to
    app code, not
    framework

    View full-size slide

  141. Monitor
    Production
    Memory

    View full-size slide

  142. https://www.dropbox.com/s/tzxhepl8o8gizw0/Screenshot%202015-11-05%2014.46.06.png?dl=0

    View full-size slide

  143. $ gem install derailed

    View full-size slide

  144. Puma
    Worker
    Killer

    View full-size slide

  145. $ gem install bullet

    View full-size slide

  146. I want a
    memory tool
    as easy to use
    as Ruby

    View full-size slide

  147. Using Heap
    Dumps to take
    Production
    snapshots

    View full-size slide

  148. Better heap
    analysis

    View full-size slide

  149. Getting this
    to work with
    Heroku

    View full-size slide

  150. Can we make
    this better?

    View full-size slide

  151. allocated memory by location
    -----------------------------------
    5131624 active_record/result.rb:116
    1336408 active_record/attribute_set/builder.rb:30
    1336088 active_record/persistence.rb:69
    1330904 active_record/core.rb:547
    1325648 active_record/attribute_methods.rb:359
    921807 active_support/callbacks.rb:81
    737376 active_record/core.rb:114
    596288 active_record/associations.rb:162
    460720 active_record/attribute_set/builder.rb:16

    View full-size slide

  152. Stack
    information in
    heap dumps
    ???

    View full-size slide

  153. What do other
    languages do
    ???

    View full-size slide

  154. https://www.dropbox.com/s/rzs0owrqq3rja96/Screenshot%202015-11-05%2014.32.52.png?dl=0

    View full-size slide

  155. https://www.dropbox.com/s/k0j31exhh2ir9oj/Screenshot%202015-11-05%2015.12.49.png?dl=0

    View full-size slide

  156. Can we
    leverage
    existing
    tooling for C?

    View full-size slide

  157. Visual
    debuggers?

    View full-size slide

  158. Further
    Reading

    View full-size slide

  159. One last thing

    View full-size slide

  160. PSA:
    Sprockets is
    now owned
    by Rails

    View full-size slide

  161. Refactors on
    the Horizon

    View full-size slide

  162. 21 commits
    makes me #4
    contributor

    View full-size slide

  163. Let’s save
    sprockets

    View full-size slide