Académique Documents
Professionnel Documents
Culture Documents
Aman Gupta
@tmm1
Ruby developers
know...
Ruby
is
fatboyke (flickr)
Ruby loves eating RAM
37prime (flickr)
this talk is about what
ruby does with your
RAM
john_lam (flickr)
Conservative
lifeisaprayer (flickr)
Stop
the
World
benimoto (flickr)
Mark
and
Sweep
michaelgoodin (flickr)
kiksbalayon (flickr)
Garbage
Collector
• conservative: the VM
hands out raw pointers to
ruby objects
• conservative: the VM
hands out raw pointers to
ruby objects
mckaysavage (flickr)
longer GC
=
less time to run
your ruby code
kgrocki (flickr)
fewer objects
=
better
performance
januskohl (flickr)
improve performance
1. remove unnecessary object allocations
object allocations are not free
improve performance
1. remove unnecessary object allocations
object allocations are not free
[ ...,
[Module, 18],
[Class, 158],
[String, 1725]
]
mayu (flickr) delgrossodotcom (flickr)
on Ruby 1.9,
use ObjectSpace.count_objects
useful tools
• ObjectSpace.each_object
• gdb.rb
• bleakhouse
• heap dumping patches
• memprof
honey_and_vinegar_real_estate (flickr)
gdb.rb: gdb hooks (gdb) ruby objects
HEAPS
SLOTS
8
1686252
• http://github.com/tmm1/gdb.rb regexp
data
class
2255
3539
3680
(0.25%)
(0.40%)
(0.41%)
hash 6196 (0.69%)
object 8785 (0.98%)
array 13850 (1.55%)
string 105350 (11.79%)
node 742346 (83.10%)
• http://github.com/tmm1/gdb.rb regexp
data
2255
3539
(0.25%)
(0.40%)
•
class 3680 (0.41%)
attach to a running REE process hash 6196 (0.69%)
and inspect the heap object
array
8785
13850
(0.98%)
(1.55%)
•
140 u 'lib'
number of arrays/hash by size 158
294
u
u
'0'
'\n'
619 u ''
• http://github.com/tmm1/gdb.rb regexp
data
2255
3539
(0.25%)
(0.40%)
•
class 3680 (0.41%)
attach to a running REE process hash 6196 (0.69%)
and inspect the heap object
array
8785
13850
(0.98%)
(1.55%)
•
140 u 'lib'
number of arrays/hash by size 158
294
u
u
'0'
'\n'
89513 __null__:__null__:__node__
41438 __null__:__null__:String
2348 site_ruby/1.8/rubygems/specification.rb:557:Array
1508 gems/specifications/gettext-1.9.gemspec:14:String
useful tools
• ObjectSpace.each_object
• gdb.rb
• bleakhouse
• heap dumping patches
• memprof
100000 file.rb:123:String
useful, 100k strings on this line
but..
sometimes it’s not enough
1571529 /tmp/ruby.heap
$ wc -l /tmp/ruby.heap
1571529 /tmp/ruby.heap
236840 memcached/memcached.rb:316
$ wc -l /tmp/ruby.heap
1571529 /tmp/ruby.heap
236840 memcached/memcached.rb:316
64952 HASH
123290 STRING
$ wc -l /tmp/ruby.heap
1571529 /tmp/ruby.heap
236840 memcached/memcached.rb:316
64952 HASH
123290 STRING
72095 int(11)
79979 varchar(255)
useful tools
• ObjectSpace.each_object
• gdb.rb
• bleakhouse
• heap dumping patches
• memprof
memprof goals
• easy to use: no patching the VM, just require
the gem
• detailed: include file/line (bleak_house),
object contents (heap dumping patch), but also
information about references between objects
• simple analysis: allow processing via various
languages and databases using simple JSON data
format
memprof
• http://github.com/ice799/memprof
• gem install memprof
• under active development on github
• works on x86_64 linux and x86_64 osx
• for best results, use an RVM built 1.8.x or REE
• ruby 1.9 support in the works
• 32bit support in the works
memprof under the hood
• rewrites your Ruby binary in memory
• injects short trampolines for all calls to internal
VM functions to do tracking
• uses libdwarf and libelf to access VM internals
like the ruby heap slabs
• uses libyajl to dump out ruby objects as json
http://timetobleed.com/string-together-global-offset-tables-to-build-a-ruby-memory-profiler/
http://timetobleed.com/memprof-a-ruby-level-memory-profiler/
http://timetobleed.com/what-is-a-ruby-object-introducing-memprof-dump/
http://timetobleed.com/hot-patching-inlined-functions-with-x86_64-asm-metaprogramming/
http://timetobleed.com/rewrite-your-ruby-vm-at-runtime-to-hot-patch-useful-features/
assembly stub*
*slightly abbreviated
100 file.rb:2:String
100 file.rb:3:Float
100 file.rb:4:Module
{
"_id": "0x19c610", memory address of object
"file": "file.rb",
"line": 2,
"type": "string",
"class": "0x1ba7f0",
"class_name": "String",
"length": 10,
"data": "helloworld"
}
Memprof.dump{
strings }
"hello" + "world"
{
"_id": "0x19c610", memory address of object
"file": "file.rb", file and line where string
"line": 2,
was created
"type": "string",
"class": "0x1ba7f0",
"class_name": "String",
"length": 10,
"data": "helloworld"
}
Memprof.dump{
strings }
"hello" + "world"
{
"_id": "0x19c610", memory address of object
"file": "file.rb", file and line where string
"line": 2,
was created
"type": "string",
"class": "0x1ba7f0", address of the class
"class_name": "String", “String”
"length": 10,
"data": "helloworld"
}
Memprof.dump{
strings }
"hello" + "world"
{
"_id": "0x19c610", memory address of object
"file": "file.rb", file and line where string
"line": 2,
was created
"type": "string",
"class": "0x1ba7f0", address of the class
"class_name": "String", “String”
"length": 4,
"data": [
1,
":b",
"0x19c750",
"0x19c598"
]
}
arrays
Memprof.dump{
[
1,
:b,
{
"_id": "0x19c5c0",
2.2,
"d"
"class": "0x1b0d18", ]
"class_name": "Array", }
"length": 4,
"data": [
1, integers and symbols are
":b", stored in the array itself
"0x19c750",
"0x19c598"
]
}
arrays
Memprof.dump{
[
1,
:b,
{
"_id": "0x19c5c0",
2.2,
"d"
"class": "0x1b0d18", ]
"class_name": "Array", }
"length": 4,
"data": [
1, integers and symbols are
":b", stored in the array itself
"0x19c750", floats and strings are
"0x19c598" separate ruby objects
]
}
hashes
Memprof.dump{
{
:a => 1,
"b" => 2.2
{ }
"_id": "0x19c598", }
"type": "hash",
"class": "0x1af170",
"class_name": "Hash",
"default": null,
"length": 2,
"data": [
[ ":a", 1 ],
[ "0xc728", "0xc750" ]
]
}
hashes
Memprof.dump{
{
:a => 1,
"b" => 2.2
{ }
"_id": "0x19c598", }
"type": "hash",
"class": "0x1af170",
"class_name": "Hash",
"default": null,
"length": 2,
"data": [
[ ":a", 1 ],
hash entries as key/value
[ "0xc728", "0xc750" ] pairs
]
}
hashes
Memprof.dump{
{
:a => 1,
"b" => 2.2
{ }
"_id": "0x19c598", }
"type": "hash",
"class": "0x1af170",
"class_name": "Hash",
"ivars": {
"@@var": 1,
"Const": 2
},
"methods": {
"world": "0x19c318"
}
}
classes
Memprof.dump{
class Hello
@@var=1
Const=2
{ def world() end
"_id": "0x19c408",
end
"type": "class", }
"name": "Hello",
"super": "0x1bfa48", superclass object reference
"super_name": "Object",
"ivars": {
"@@var": 1,
"Const": 2
},
"methods": {
"world": "0x19c318"
}
}
classes
Memprof.dump{
class Hello
@@var=1
Const=2
{ def world() end
"_id": "0x19c408",
end
"type": "class", }
"name": "Hello",
"super": "0x1bfa48", superclass object reference
"super_name": "Object",
# in environment.rb
require `gem which memprof/signal`.strip
plugging a leak
in rails3
plugging a leak
in rails3
send the app some
requests so it leaks
$ ab -c 1 -n 30
http://localhost:3000/
plugging a leak
in rails3
send the app some
requests so it leaks
$ ab -c 1 -n 30
http://localhost:3000/
holding references
to all controllers
find references to object
holding references
to all controllers
• In development mode, Rails reloads all your
application code on every request
• ActionView::Partials::PartialRenderer is caching
partials used by each controller as an optimization
• But.. it ends up holding a reference to every single
reloaded version of those controllers
• In development mode, Rails reloads all your
application code on every request
• ActionView::Partials::PartialRenderer is caching
partials used by each controller as an optimization
• But.. it ends up holding a reference to every single
reloaded version of those controllers
more* memprof features
• memprof.trace
• memprof::tracer
{
"time": 4.3442,
"rails": {
"controller": "test",
"action": "index"
},
"request": {
"REQUEST_PATH": "/test,,
"REQUEST_METHOD": "GET"
},
config.middleware.use(Memprof::Tracer)
{
"time": 4.3442, total time for request
"rails": {
"controller": "test",
"action": "index"
},
"request": {
"REQUEST_PATH": "/test,,
"REQUEST_METHOD": "GET"
},
config.middleware.use(Memprof::Tracer)
{
"time": 4.3442, total time for request
"request": {
"REQUEST_PATH": "/test,,
"REQUEST_METHOD": "GET"
},
config.middleware.use(Memprof::Tracer)
{
"time": 4.3442, total time for request
"mysql": {
"queries": 3, 3 mysql queries
"time": 0.00109302
},
"gc": {
"calls": 8,
"time": 2.04925
},
config.middleware.use(Memprof::Tracer)
"mysql": {
"queries": 3, 3 mysql queries
"time": 0.00109302
},
"gc": {
"calls": 8, 8 calls to GC
"time": 2.04925
},
config.middleware.use(Memprof::Tracer)
"mysql": {
"queries": 3, 3 mysql queries
"time": 0.00109302
},
"gc": {
"calls": 8, 8 calls to GC
"time": 2.04925 2 secs spent in GC
},
config.middleware.use(Memprof::Tracer)
"objects": {
"created": 3911103, 3 million objs created
"types": {
"none": 1168831,
"object": 1127,
"float": 627,
"string": 1334637,
"array": 609313,
"hash": 3676,
"match": 70211
}
}
}
config.middleware.use(Memprof::Tracer)
"objects": {
"created": 3911103, 3 million objs created
"types": {
"none": 1168831,
"object": 1127,
"float": 627,
"string": 1334637, lots of strings
"array": 609313,
"hash": 3676,
"match": 70211
}
}
}
config.middleware.use(Memprof::Tracer)
"objects": {
"created": 3911103, 3 million objs created
"types": {
"none": 1168831,
"object": 1127,
"float": 627,
"string": 1334637, lots of strings
"array": 609313, lots of arrays
"hash": 3676,
"match": 70211
}
}
}
config.middleware.use(Memprof::Tracer)
"objects": {
"created": 3911103, 3 million objs created
"types": {
"none": 1168831,
"object": 1127,
"float": 627,
"string": 1334637, lots of strings
"array": 609313, lots of arrays
"hash": 3676,
"match": 70211 regexp matches
}
}
}
config.middleware.use(Memprof::Tracer)
"objects": {
"created": 3911103, 3 million objs created
"types": {
"none": 1168831,
"object": 1127, object instances
"float": 627,
"string": 1334637, lots of strings
"array": 609313, lots of arrays
"hash": 3676,
"match": 70211 regexp matches
}
}
}
config.middleware.use(Memprof::Tracer)
"objects": {
"created": 3911103, 3 million objs created
"types": {
"none": 1168831, 1 million method calls
"object": 1127, object instances
"float": 627,
"string": 1334637, lots of strings
"array": 609313, lots of arrays
"hash": 3676,
"match": 70211 regexp matches
}
}
}
config.middleware.use(Memprof::Tracer)
mckaysavage (flickr)
longer GC
=
less time to run
your ruby code
kgrocki (flickr)
fewer objects
=
better
performance
januskohl (flickr)
Use these tools.
Questions?
Aman Gupta
@tmm1
http://scribd.com/tmm1