Class: HeapScope::Aging
- Inherits:
-
Object
- Object
- HeapScope::Aging
- Defined in:
- lib/heapscope/aging.rb
Overview
Approximate object aging across forced GC cycles. Does not invent ages the runtime cannot support — labels are coarse.
Defined Under Namespace
Classes: Sample
Constant Summary collapse
- BUCKETS =
%i[young middle_aged long_lived unknown].freeze
Instance Method Summary collapse
- #classify(class_name) ⇒ Object
-
#initialize(runtime: Runtime.current) ⇒ Aging
constructor
A new instance of Aging.
- #push_counts(cycle:, counts:, generation_hint: nil) ⇒ Object
- #report(top: 20) ⇒ Object
- #sample!(cycle:) ⇒ Object
Constructor Details
Instance Method Details
#classify(class_name) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/heapscope/aging.rb', line 38 def classify(class_name) series = @samples.map { |s| s.class_counts[class_name].to_i } return { bucket: :unknown, series: series } if series.size < 2 first = series.first last = series.last survived_ratio = first.positive? ? last.to_f / first : 0.0 bucket = if survived_ratio < 0.2 :young elsif survived_ratio < 0.7 :middle_aged else :long_lived end { class: class_name, bucket: bucket, series: series, survived_ratio: survived_ratio.round(4), note: "Coarse age bucket from multi-cycle survival — not exact object age." } end |
#push_counts(cycle:, counts:, generation_hint: nil) ⇒ Object
33 34 35 36 |
# File 'lib/heapscope/aging.rb', line 33 def push_counts(cycle:, counts:, generation_hint: nil) @samples << Sample.new(cycle_index: cycle, class_counts: counts, generation_hint: generation_hint) self end |
#report(top: 20) ⇒ Object
63 64 65 66 67 68 69 |
# File 'lib/heapscope/aging.rb', line 63 def report(top: 20) names = @samples.flat_map { |s| s.class_counts.keys }.uniq names.map { |n| classify(n) } .select { |r| r[:series].last.to_i > 10 } .sort_by { |r| -r[:survived_ratio] } .first(top) end |
#sample!(cycle:) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/heapscope/aging.rb', line 16 def sample!(cycle:) counts = Hash.new(0) if @runtime.each_object? @runtime.each_object do |obj| name = begin obj.class.name rescue StandardError nil end next unless name counts[name] += 1 end end push_counts(cycle: cycle, counts: counts, generation_hint: (GC.stat[:count] if GC.respond_to?(:stat))) end |