Skip to content

Instantly share code, notes, and snippets.

@ssimeonov
Created July 18, 2013 07:06
Show Gist options
  • Save ssimeonov/6027269 to your computer and use it in GitHub Desktop.
Save ssimeonov/6027269 to your computer and use it in GitHub Desktop.
Analyze dyno memory use on Heroku with this memory footprint analyzer.
# Memory snapshot analyzer which parses the /proc file system on *nix
#
# Example (run in Heroku console):
#
# ms = MemorySnapshot.new
# 1.upto(10000).map { |i| Array.new(i) }; nil
# ms.snapshot!; nil
# ms.diff 10
# => {"lib/ld-2.11.1.so"=>156, "heap"=>2068, "all"=>2224}
#
class MemorySnapshot
attr_reader :previous
attr_reader :current
def initialize
snapshot!
@previous = @current
end
# Generates a Hash of memory elements mapped to sizes of the elements in Kb
def snapshot!
@previous = @current
@current = reduce(names_with_sizes)
end
# Calculates the difference between the previous and the current snapshot
# Threshold is a minimum delta in kilobytes required to include an entry
def diff(threshold = 0)
self.class.diff_between previous, current, threshold
end
# Calculates the difference between two memory snapshots
# Threshold is a minimum delta in kilobytes required to include an entry
def self.diff_between(before, after, threshold)
names = (before.keys + after.keys).uniq
names.reduce({}) do |memo, name|
delta = after.fetch(name) { 0 } - before.fetch(name) { 0 }
memo[name] = delta if delta.abs >= threshold
memo
end
end
private
def reduce(matches)
total = 0
current_name = nil
matches.reduce(Hash.new { 0 }) do |memo, match|
current_name = match[:name] || current_name
size = match[:size].to_i
total += size
memo[current_name] += size
memo
end.tap { |snapshot| snapshot['all'] = total }
end
def names_with_sizes
smap_entries.map do |line|
/((^(\/|\[)(?<name>[^ \]]+)\]?\s+)|(^))(?<size>\d+)\s/.match(line)
end
end
def smap_entries
smaps.
gsub(/^(([^Sa-f0-9])|(S[^i]))[^\n]+\n/m, '').
gsub(/\nSize:/m, '').
gsub(/[0-9a-f]+-[0-9a-f]+.{6}[0-9a-f]+ [0-9a-f]+:[0-9a-f]+ [0-9a-f]+\s+/i, '').
split("\n")
end
def smaps
File.read("/proc/#{Process.pid}/smaps")
end
end
@wowremywang
Copy link

Hi @ssimeonov, thank you for sharing this.
I am running 2 dynos on Heroku App - Web dyno and Sidekiq worker dyno.
But I am not sure how I can check Sidekiq worker dyno memory usage using this class.
Can you help me to check?

Thank you.

Copy link

ghost commented Aug 7, 2020

Same here. Not sure how I can use this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment