Share
Embed
Ruby Objects, A Walkabout
In Ruby, it's easy to create classes, methods, and objects. For instance, did you know that a hello world sinatra app uses 43 classes, 155 methods, and dispatches 548 methods for a single request. In this talk, we're going use TracePoint API to look at how you can find out this information among other things. Additionally, now that we have all these objects from our Ruby app, the evolving ObjectSpace API we can glean information about rough object size in memory. Let's take a walk through objects in Ruby.
hone
March 05, 2015
Transcript
92 slides
-
enable trace set_trace_func proc {|event, file, line, id, binding, klass|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, klass } -
code to trace set_trace_func proc {|event, file, line, id, binding,
klass| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar -
disable trace set_trace_func proc {|event, file, line, id, binding, klass|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s
%s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s
%s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s
%s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s
%s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s
%s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class
c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel -
set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s
%s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new -
events • c-call • c-return • call • return •
class • end • line • raise -
tracepoint = TracePoint.new do |tp| printf "%8s %s:%-2d %10s %8s\n",
tp.event, tp.path, tp.lineno, tp.method_id, tp.defined_class end tracepoint.enable do class Foo def bar "baz" end end Foo.new.bar end Foo.new -
tracepoint = TracePoint.new do |tp| printf "%8s %s:%-2d %10s %8s\n",
tp.event, tp.path, tp.lineno, tp.method_id, tp.defined_class end tracepoint.enable do class Foo def bar "baz" end end Foo.new.bar end Foo.new -
tracepoint = TracePoint.new do |tp| printf "%8s %s:%-2d %10s %8s\n",
tp.event, tp.path, tp.lineno, tp.method_id, tp.defined_class end tracepoint.enable do class Foo def bar "baz" end end Foo.new.bar end Foo.new -
b_call tracepoint.rb:5 line tracepoint.rb:6 c_call tracepoint.rb:6 inherited Class c_return tracepoint.rb:6
inherited Class class tracepoint.rb:6 line tracepoint.rb:7 c_call tracepoint.rb:7 method_added Module c_return tracepoint.rb:7 method_added Module end tracepoint.rb:10 line tracepoint.rb:12 c_call tracepoint.rb:12 new Class c_call tracepoint.rb:12 initialize BasicObject c_return tracepoint.rb:12 initialize BasicObject c_return tracepoint.rb:12 new Class call tracepoint.rb:7 bar Foo line tracepoint.rb:8 bar Foo return tracepoint.rb:9 bar Foo b_return tracepoint.rb:13 -
TracePoint Middleware def call(env) stats = {} response = nil
trace = TracePoint.new(:call) do |tp| stats[tp.defined_class] ||= {} stats[tp.defined_class][tp.method_id] ||= 0 stats[tp.defined_class][tp.method_id] += 1 end trace.enable { response = @app.call(env) } ... end * https://gist.github.com/mattetti/5097206 -
TracePoint Middleware def call(env) ... puts env['PATH_INFO'] puts "#{stats.keys.size} classes
used" puts "#{stats.map{|k,v| v.keys}.flatten.size} methods used" puts "#{stats.map{|k,v| v.values}.flatten.inject(0) {|sum, num| sum + num }} methods dispatched" File.open("tmp/#{env['PATH_INFO'].gsub('/', '_')}_req_stats. json", "w"){|f| f << stats.to_json } puts "" response end * https://gist.github.com/mattetti/5097206 -
rack require 'rack/tracepoint' class RubyVersion def self.version `ruby -v` end
end use Rack::TracePoint::Middleware run Proc.new {|env| [200, {"Content-Type" => "text/plain"}, [RubyVersion.version] ] } -
sinatra require 'sinatra' require 'rack/tracepoint' use Rack::Tracepoint::Middleware get "/" do
`ruby -v` end run Sinatra::Application -
rails # app/controllers/welcome_controller.rb class WelcomeController < ApplicationController def index @ruby_version
= `ruby -v` end end # app/views/welcome/index.html.erb <%= @ruby_version %> -
{ "Sinatra::Base": { "initialize": 1, "call": 1, "call!": 1, "indifferent_params":
1, "indifferent_hash": 1, "settings": 9, "force_encoding": 1, "invoke": 3, "dispatch!": 1, "filter!": 4, "route!": 1, "process_route": 1, "route_eval": 1, "error_block!": 1 } } -
{ "ERB::Compiler::Buffer#push": 133, "ERB::Compiler#add_put_cmd": 66, "ERB::Compiler#content_dump": 66, "ERB::Compiler#add_insert_cmd": 34, "Rack::Builder#use":
13, "#<Class:Sinatra::Base>#settings": 10, "Sinatra::Base#settings": 9, "Rack::Utils::HeaderHash#[]": 9, "#<Class:Sinatra::Application>#app_file": 8, "Rack::Utils::HeaderHash#[]=": 7, "Rack::Protection::Base#initialize": 6, "Rack::Protection::Base#default_options": 6, "#<Class:Sinatra::Base>#environment": 4, "#<Class:Sinatra::Base>#root": 4, "Sinatra::Base#filter!": 4, "Sinatra::Base#invoke": 3 ... -
ObjectSpace.count_objects { "TOTAL": 30161, "FREE": 677, "T_OBJECT": 70, "T_CLASS": 616,
"T_MODULE": 37, "T_FLOAT": 7, "T_STRING": 10950, "T_REGEXP": 77, "T_ARRAY": 1629, "T_HASH": 63, "T_STRUCT": 9, "T_BIGNUM": 2, "T_FILE": 29, "T_DATA": 983, "T_MATCH": 26, ... } -
ObjectSpace#each_object require 'json' types_count = Hash.new(0) ObjectSpace.each_object do |o| types_count[o.class]
+= 1 end puts types_count.to_a.sort {|a, b| b[1] <=> a[1] }.to_h.to_json -
{ "String": 8199, "RubyVM::InstructionSequence": 750, "Array": 511, "Class": 288, "Encoding":
100, "Regexp": 77, "Hash": 44, "Module": 37, "MatchData": 20, "File": 18, "Gem::Requirement": 15, "Gem::Version": 15, "Gem::StubSpecification": 11, "Proc": 10, "Range": 9, "Gem::StubSpecification::StubLine": 8, "Float": 7 -
Object Introspection require 'objspace' os = ObjectSpace o = nil
os.trace_object_allocations do class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o) -
require 'objspace' os = ObjectSpace o = nil os.trace_object_allocations do
class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o) -
require 'objspace' os = ObjectSpace o = nil os.trace_object_allocations do
class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o) -
require 'objspace' os = ObjectSpace o = nil os.trace_object_allocations do
class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o) -
Object Size Counting require 'objspace' Metadata = Struct.new(:class, :sourcefile, :sourceline,
: class_path, :method_id, :memsize) objects = [] result = {} def walk_objects(result) ObjectSpace.each_object do |o| result[o.object_id] = Metadata.new( ObjectSpace.allocation_class_path(o), ObjectSpace.allocation_sourcefile(o), ObjectSpace.allocation_sourceline(o), ObjectSpace.allocation_class_path(o), ObjectSpace.allocation_method_id(o), ObjectSpace.memsize_of(o) ) end end -
Object Size Counting ObjectSpace.trace_object_allocations do a = Foo.new.bar b =
Baz.new.foo walk_objects(result) end result.to_a.sort {|a,b| b[1].memsize <=> a[1].memsize }. to_h.each do |object_id, s| next if s.class_path.nil? printf "%s:%-2d %5s#%s %d\n", s.sourcefile, s.sourceline, s. class_path, s.method_id, s.memsize end -
Made Up Algorithm def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do
|i| a[i], a[i + pivot] = a[i + pivot], a[i] end end N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) -
ObjectSpace::AllocationTracer.trace def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i],
a[i + pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) } -
{["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0,
0, 0, 0, 0], ["algorithm.rb", 7]=>[5000, 0, 0, 0, 0, 0]} -
{["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0,
0, 0, 0, 0], ["algorithm.rb", 7]=>[5000, 0, 0, 0, 0, 0]} -
def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i
+ pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) } -
{["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0,
0, 0, 0, 0], ["algorithm.rb", 7]=>[5000, 0, 0, 0, 0, 0]} -
def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i
+ pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) } -
def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i
+ pivot] = a[i + pivot], a[i] end end ObjectSpace::AllocationTracer.setup(%i{path line type}) pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) } -
{["algorithm.rb", 15, :T_NODE]=>[2, 0, 2, 1, 1, 40], ["algorithm.rb", 7,
:T_ARRAY]=>[5000, 0, 2511, 0, 1, 45560], ["algorithm.rb", 15, :T_DATA]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_ARRAY]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_STRING]=>[10000, 0, 10000, 1, 1, 0], ["algorithm.rb", 6, :T_STRUCT]=>[1, 0, 1, 1, 1, 0]} -
{["algorithm.rb", 15, :T_NODE]=>[2, 0, 2, 1, 1, 40], ["algorithm.rb", 7,
:T_ARRAY]=>[5000, 0, 2511, 0, 1, 45560], ["algorithm.rb", 15, :T_DATA]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_ARRAY]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_STRING]=>[10000, 0, 10000, 1, 1, 0], ["algorithm.rb", 6, :T_STRUCT]=>[1, 0, 1, 1, 1, 0]} -
{["algorithm.rb", 15, :T_NODE]=>[2, 0, 2, 1, 1, 40], ["algorithm.rb", 7,
:T_ARRAY]=>[5000, 0, 2511, 0, 1, 45560], ["algorithm.rb", 15, :T_DATA]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_ARRAY]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_STRING]=>[10000, 0, 10000, 1, 1, 0], ["algorithm.rb", 6, :T_STRUCT]=>[1, 0, 1, 1, 1, 0]} -
def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i
+ pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) } -
def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| x, y
= a[i + pivot], a[i] a[i] = x a[i + pivot] = y end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }