class Rack::MiniProfiler::GCProfiler
Public Class Methods
Source
# File lib/mini_profiler/gc_profiler.rb, line 5 def initialize @ignore = [] @ignore << @ignore.__id__ end
Public Instance Methods
Source
# File lib/mini_profiler/gc_profiler.rb, line 63 def analyze_growth(ids_before, ids_after) new_objects = 0 memory_allocated = 0 ids_after.each do |id, _| if !ids_before.include?(id) && obj = ObjectSpace._id2ref(id) # this is going to be version specific (may change in 2.1) size = ObjectSpace.memsize_of(obj) memory_allocated += size new_objects += 1 end end [new_objects, memory_allocated] end
Source
# File lib/mini_profiler/gc_profiler.rb, line 79 def analyze_initial_state(ids_before) memory_allocated = 0 objects = 0 ids_before.each do |id, _| if obj = ObjectSpace._id2ref(id) # this is going to be version specific (may change in 2.1) memory_allocated += ObjectSpace.memsize_of(obj) objects += 1 end end [objects, memory_allocated] end
Source
# File lib/mini_profiler/gc_profiler.rb, line 51 def analyze_strings(ids_before, ids_after) result = {} ids_after.each do |id, _| obj = ObjectSpace._id2ref(id) if String === obj && !ids_before.include?(obj.object_id) result[obj] ||= 0 result[obj] += 1 end end result end
Source
# File lib/mini_profiler/gc_profiler.rb, line 39 def diff_object_stats(before, after) diff = {}.compare_by_identity after.each do |k, v| diff[k] = v - before[k] end before.each do |k, v| diff[k] = 0 - v unless after.has_key?(k) end diff end
Source
# File lib/mini_profiler/gc_profiler.rb, line 10 def object_space_stats stats = Hash.new(0).compare_by_identity ids = Hash.new.compare_by_identity @ignore << stats.__id__ @ignore << ids.__id__ ObjectSpace.each_object { |o| begin stats[o.class] += 1 ids[o.__id__] = o if Integer === o.__id__ rescue NoMethodError # protect against BasicObject end } @ignore.each do |id| if ids.delete(id) klass = ObjectSpace._id2ref(id).class stats[klass] -= 1 end end result = { stats: stats, ids: ids } @ignore << result.__id__ result end
Source
# File lib/mini_profiler/gc_profiler.rb, line 94 def profile_gc(app, env) # for memsize_of require 'objspace' # clean up before GC.start stat = GC.stat prev_gc_state = GC.disable stat_before = object_space_stats b = app.call(env)[2] b.close if b.respond_to? :close stat_after = object_space_stats # so we don't blow out on memory prev_gc_state ? GC.disable : GC.enable diff = diff_object_stats(stat_before[:stats], stat_after[:stats]) string_analysis = analyze_strings(stat_before[:ids], stat_after[:ids]) new_objects, memory_allocated = analyze_growth(stat_before[:ids], stat_after[:ids]) objects_before, memory_before = analyze_initial_state(stat_before[:ids]) body = [] body << " Overview -------- Initial state: object count: #{objects_before} Memory allocated outside heap (bytes): #{memory_before} GC Stats: -------- #{stat.map { |k, v| "#{k} : #{v}" }.sort!.join("\n")} New bytes allocated outside of Ruby heaps: #{memory_allocated} New objects: #{new_objects} " body << " ObjectSpace delta caused by request: -----------------------------------\n" diff.to_a.delete_if { |_k, v| v == 0 }.sort_by! { |_k, v| v }.reverse_each do |k, v| body << "#{k} : #{v}\n" end body << "\n ObjectSpace stats: -----------------\n" stat_after[:stats].to_a.sort_by! { |_k, v| v }.reverse_each do |k, v| body << "#{k} : #{v}\n" end body << "\n String stats: ------------\n" string_analysis.to_a.sort_by! { |_k, v| -v }.take(1000).each do |string, count| body << "#{count} : #{string}\n" end [200, { 'Content-Type' => 'text/plain' }, body] ensure prev_gc_state ? GC.disable : GC.enable end