Skip to content

Instantly share code, notes, and snippets.

@andrepiske
Last active July 4, 2023 14:01
Show Gist options
  • Select an option

  • Save andrepiske/fb49dedc8f37e529fd485d93baab7e5e to your computer and use it in GitHub Desktop.

Select an option

Save andrepiske/fb49dedc8f37e529fd485d93baab7e5e to your computer and use it in GitHub Desktop.
my_memory_tracker.rb
class MyMemoryTracker
attr_reader :klasses, :memories, :obj_ids
def initialize(perform_gc=false, save_object_ids: false)
@save_object_ids = save_object_ids
@default_perform_gc = perform_gc
@my_obj_ids = {}
@iterations = 0
@klasses = []
@memories = []
@obj_ids = []
@klass_mt = Module.instance_method(:class)
@objid_mt = BasicObject.instance_method(:__id__)
ignore_ids(@my_obj_ids, @klasses, @memories, @obj_ids)
end
def summary_introduced_at(iter_no)
objects_introduced_at(iter_no)[:grp].map do |klass, values|
[klass, values.map { |x| ObjectSpace.memsize_of(x) }.sum]
end.sort_by { |_, sz| -sz }
end
def objects_introduced_at(iter_no, filter_klass=nil)
return false unless @save_object_ids
objs = Set.new
isa = Module.instance_method(:is_a?)
ObjectSpace.each_object do |obj|
next if filter_klass != nil && obj.isa.bind_call(obj, filter_klass)
oid = @objid_mt.bind_call(obj)
next unless @obj_ids[iter_no].include?(oid)
next if iter_no > 0 && @obj_ids[iter_no - 1].include?(oid)
objs << obj
end
grp = objs.group_by { |o| @klass_mt.bind_call(o) }.to_a.sort_by { |k, os| -os.length }.to_h
{ objs:, grp: }
end
def snapshot!(perform_gc=nil)
do_the_gc(false) if perform_gc != false && (perform_gc || @default_perform_gc)
usg = Hash.new { |h, x| h[x] = 0 }
klasses = Hash.new { |h, x| h[x] = 0 }
oids = @save_object_ids ? Set.new : nil
ignore_ids(usg, klasses)
ignore_ids(oids) if oids != nil
ObjectSpace.each_object do |obj|
oid = @objid_mt.bind_call(obj )
next if @my_obj_ids[oid]
kl = @klass_mt.bind_call(obj)
klasses[kl] += 1
usg[kl] += ObjectSpace.memsize_of(obj)
oids << oid if @save_object_ids
end
@klasses << klasses
@memories << usg
@obj_ids << oids if @save_object_ids
@iterations += 1
[@iterations - 1, klasses.values.sum, usg.values.sum]
end
def do_the_gc(printing=true)
puts `free -h` if printing
3.times { GC.start }
puts `free -h` if printing
end
def delta(da=0, db=nil)
db = @iterations - 1 if db == nil
k0, k1, u0, u1 = @klasses[da], @klasses[db], @memories[da], @memories[db]
kdelta = (k1.keys | k0.keys).map { |kl| [kl, k1.fetch(kl, 0) - k0.fetch(kl, 0)] }.select { |_, dl| dl != 0 }.sort_by {|_, dl| dl}
udelta = (u1.keys | u0.keys).map { |kl| [kl, u1.fetch(kl, 0) - u0.fetch(kl, 0)] }.select { |_, dl| dl != 0 }.sort_by {|_, dl| dl}
{ kdelta:, udelta: }
end
def ignore_ids(*objs)
objs.each { |o| @my_obj_ids[@objid_mt.bind_call(o)] = true }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment