Class: Heapy::Diff

Inherits:
Object
  • Object
show all
Defined in:
lib/heapy/diff.rb

Overview

Diff 2 dumps example:

Heapy::Diff.new(before: 'my_dump_1.json', after: 'my_dump_2.json').call

This will find objects that are present in my_dump_2 that are not present in my_dump_1 this means they were allocated sometime between the two heap dumps.

Diff 3 dumps example:

Heapy::Diff.new(before: 'my_dump_1.json', after: 'my_dump_2.json', retained: 'my_dump_3.json').call

This will find objects that are present in my_dump_2 that are not present in my_dump_1 but only if the objects are still present at the time that my_dump_3 was taken. This does not guarantee that they’re retained forever, but were still present at the time the last dump was taken.

You can output the diff of heap dumps by passing in a filename as ‘output_diff` for example

Heapy::Diff.new(before: 'my_dump_1.json', after: 'my_dump_2.json', outpu_diff: 'out.json').call

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(before:, after:, retained: nil, io: STDOUT, output_diff: nil) ⇒ Diff

Returns a new instance of Diff.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/heapy/diff.rb', line 27

def initialize(before:, after:, retained: nil, io: STDOUT, output_diff: nil)
  @before_file = before
  @after_file = after
  @retained_file = retained
  @output_diff_file = output_diff ? File.open(output_diff, "w+") : nil
  @io = io
  @diff = Hash.new { |hash, k|
    hash[k] = {}
    hash[k]["count"] = 0
    hash[k]["memsize"] = 0
    hash[k]
  }

  @before_address_hash = {}
  @retained_address_hash = {}
end

Instance Attribute Details

#diffObject (readonly)

Returns the value of attribute diff.



25
26
27
# File 'lib/heapy/diff.rb', line 25

def diff
  @diff
end

Instance Method Details

#callObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/heapy/diff.rb', line 45

def call
  read(@before_file) { |parsed| @before_address_hash[parsed['address']] = true }
  read(@retained_file) { |parsed| @retained_address_hash[parsed['address']] = true } if @retained_file

  read(@after_file) do |parsed, original_line|
    address = parsed['address']
    next if previously_allocated?(address)
    next if not_retained?(address)

    @output_diff_file.puts original_line if @output_diff_file

    hash = diff["#{parsed['type']},#{parsed['file']},#{parsed['line']}"]
    hash["count"] += 1
    hash["memsize"] += parsed["memsize"] || 0
    hash["type"] ||= parsed["type"]
    hash["file"] ||= parsed["file"]
    hash["line"] ||= parsed["line"]
  end

  @output_diff_file.close if @output_diff_file
  @before_address_hash.clear
  @retained_address_hash.clear

  total_memsize = diff.inject(0){|sum,(_,v)| sum + v["memsize"] }

  diff.sort_by do |k,v|
    v["count"]
  end.reverse.each do |key, data|
    @io.puts "#{@retained_file ? "Retained" : "Allocated"} #{data['type']} #{data['count']} objects of size #{data['memsize']}/#{total_memsize} (in bytes) at: #{data['file']}:#{data['line']}"
  end

  @io.puts "\nWriting heap dump diff to #{@output_diff_file.path}\n" if @output_diff_file
end