Skip to content

Commit

Permalink
added object lifetimes function.
Browse files Browse the repository at this point in the history
  • Loading branch information
authorNari committed Mar 14, 2009
1 parent 1510047 commit 0aec0ee
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 28 deletions.
6 changes: 5 additions & 1 deletion README
Expand Up @@ -6,11 +6,15 @@ GC::Profiler module was added at ruby1.9.1.
== Install

> git clone git://github.com/authorNari/gc_profiler_on_graph.git vendor/plugins/gc_profiler_on_graph
> ruby script/runner vendor/plugins/gc_profiler_on_graph
> ruby script/runner vendor/plugins/gc_profiler_on_graph/install.rb

If you use Rails 2.1 or later, you can install with this command.

> ruby script/plugin install git://github.com/authorNari/gc_profiler_on_graph.git

And please wirtten to layout/xxxx.html.erb

<%= javascript_include_tag :all %>

= Author
Nakamura Narirhiro<authorNari at gmail.com>
10 changes: 4 additions & 6 deletions install.rb
@@ -1,10 +1,8 @@
# Copy html5jp JavaScript codes to public/javascripts.

Dir.chdir(File.join(RAILS_ROOT, 'vendor', 'plugins', 'html5jp_graphs')) do
dest_dir = File.join(RAILS_ROOT, 'public', 'javascripts')
FileUtils.mkdirs(dest_dir) unless File.exist?(dest_dir)
dest_dir = File.join(RAILS_ROOT, 'public', 'javascripts')
FileUtils.mkdirs(dest_dir) unless File.exist?(dest_dir)

Dir.glob("html5jp/html5jp/**/*.js").each do |path|
FileUtils.cp(path, dest_dir) unless File.exist?(File.join(dest_dir, File.basename(path)))
end
Dir.glob(File.join(RAILS_ROOT, File.dirname(__FILE__), "html5jp/html5jp/**/*.js")).each do |path|
FileUtils.cp(path, dest_dir) unless File.exist?(File.join(dest_dir, File.basename(path)))
end
47 changes: 26 additions & 21 deletions lib/gc_profiler_on_graph.rb
Expand Up @@ -20,37 +20,23 @@ def self.insert_graphs
script_html = <<-HTML
<script type="text/javascript">
window.onload = function() {
var heap_graph = new html5jp.graph.vbar("heap_vbar");
if(!heap_graph ) { return; }
#{display_draw_heap_params(profile)}
heap_graph.draw(items, params);
var gctime_graph = new html5jp.graph.line("gctime_line");
if(!gctime_graph ) { return; }
#{display_draw_gctime_params(profile)}
gctime_graph.draw(items, params);
var stats_graph = new html5jp.graph.vbar("stats_vbar");
if(!stats_graph ) { return; }
#{display_draw_heap_stats_params}
stats_graph.draw(items, params);
#{template_draw_graph(:heap_vbar, :vbar){ display_draw_heap_params(profile) }}
#{template_draw_graph(:gctime_line, :line){ display_draw_gctime_params(profile) }}
#{template_draw_graph(:stats_vbar, :vbar){ display_draw_heap_stats_params }}
#{template_draw_graph(:lifetime_vbar, :vbar){ display_draw_lifetimes } if ObjectSpace.respond_to?(:count_object_lifetimes)}
};
</script>
HTML
canvas_html = <<-HTML
<table><tr>
<td colspan="3"><div><canvas width="1300" height="400" id="stats_vbar"></canvas></div></td>
</tr><tr>
<td colspan="3"><div><canvas width="1300" height="400" id="lifetime_vbar"></canvas></div></td>
</tr><tr>
<td><div><canvas width="500" height="400" id="heap_vbar"></canvas></div></td>
<td><div><canvas width="500" height="400" id="gctime_line"></canvas></div></td>
<td></td>
</tr><tr>
</tr></table>
HTML
insert_text :before, /<\/head>/i, script_html
Expand Down Expand Up @@ -95,6 +81,13 @@ def self.display_draw_heap_stats_params
res += template_xy(%Q!['type', "#{stats.map{|k, v| k}.join('", "')}"]!, "['object count']")
end

def self.display_draw_lifetimes
res = []
stats = ObjectSpace.count_object_lifetimes.reject!{|k, v| %w(TOTAL FREE).include? k.to_s }.sort_by{|e| e[1]}
res = template_items "['lifetime', #{stats.map{|k,v| v}.join(', ')}]"
res += template_xy(%Q!['type', "#{stats.map{|k, v| k}.join('", "')}"]!, "['life time']")
end

def self.template_items(cols)
res = <<-HTML
var items = [
Expand All @@ -112,6 +105,18 @@ def self.template_xy(x, y)
HTML
end

def self.template_draw_graph(id, type, &block)
graph_var = "#{id}_graph"
res = <<-HTML
var #{graph_var} = new html5jp.graph.#{type}("#{id}");
if(!#{graph_var} ) { return; }
#{block.call}
#{graph_var}.draw(items, params);
HTML
end

def self.insert_text(position, pattern, new_text)
index = case pattern
when Regexp
Expand Down

6 comments on commit 0aec0ee

@jeremy
Copy link

@jeremy jeremy commented on 0aec0ee Dec 5, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only reference I've seen to ObjectSpace.count_object_lifetimes. Is it a custom extension? What does it measure?

@authorNari
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch of ObjectSpace.count_object_lifetimes is here.
https://gist.github.com/79030

This method returns {T_XXX => 'Average GC count of a T_XXX object', T_XXX => ...}. But, I didn't commit to Ruby's trunk because GC was slightly slow by this patch.

@jeremy
Copy link

@jeremy jeremy commented on 0aec0ee Dec 5, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you -- this is very useful! It doesn't seem much slower, either.

I'd be happy to trade some performance for greater knowledge about the live objects in the heap.

@authorNari
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I'll propose a method that is like this to ML :)

I have one question. Do you have any use-case for this method?
e.g. finding some memory leaks...

@jeremy
Copy link

@jeremy jeremy commented on 0aec0ee Dec 6, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly. For example, we can look for leaks by watching growth of ObjectSpace.count_object_lifetimes[:T_ARRAY] / Object.count_objects[:T_ARRAY]

A histogram of object lifetimes would be even more useful. Split lifetimes into buckets (log scale) and count objects in each bucket. That gives an even clearer picture of object churn (generating temporary garbage) versus long-lived objects (loaded Ruby code, singletons, loaded YAML) versus leaks (unbounded cache, stray references to temporary objects) and show how they change over time.

Then leaks would show up as increasing numbers of aging objects.

@authorNari
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha, this histogram is useful! Thanks for your idea!

Please sign in to comment.