class Knj::Memory_analyzer
This class contains methods to debug memory-leaks. It prints its collected information as HTML.
Examples¶ ↑
ma = Knj::Memory_analyzer.new ma.write
Public Class Methods
Initialized various objects.
# File lib/knj/memory_analyzer.rb, line 7 def initialize @printed = {} end
Public Instance Methods
Collects and writes a lot of information about the spawned arrays as HTML to the given IO.
# File lib/knj/memory_analyzer.rb, line 109 def arrays(to = $stdout) arrays = {} ObjectSpace.each_object(Array) do |arr| begin arr = arr.sort rescue ArgumentError #When unable to sort regexps... next end keys = [] arr.each do |key| keys << key.class.name.to_s end if keys.empty? keystr = :empty else keystr = keys.join(":") end if !arrays.key?(keystr) arrays[keystr] = 1 else arrays[keystr] += 1 end end arrays.delete_if do |key, val| val < 100 end arrays = Knj::ArrayExt.hash_sort(arrays) do |h1, h2| h2[1] <=> h1[1] end to.write "<h1>Arrays</h1>\n" to.write "<table class=\"arrays list\">\n" to.write "\t<thead>\n" to.write "\t\t<tr>\n" to.write "\t\t\t<th>Array classes</th>\n" to.write "\t\t\t<th>Instances</th>\n" to.write "\t\t</tr>\n" to.write "\t</thead>\n" to.write"\t<tbody>\n" arrays.each do |key, val| to.write "\t\t<tr>\n" to.write "\t\t\t<td>#{Knj::Web.html(key)}</td>\n" to.write "\t\t\t<td>#{Knj::Locales.number_out(val, 0)}</td>\n" to.write "\t\t</tr>\n" end to.write "\t</tbody>\n" to.write "</table>\n" end
Collects information about the spawned classes and writes it as HTML to the given IO.
# File lib/knj/memory_analyzer.rb, line 212 def constants(to = $stdout) to.print "<h1>Constants</h1>\n" to.print "<table class=\"memory_analyzer list\">\n" to.print "\t<thead>\n" to.print "\t\t<tr>\n" to.print "\t\t\t<th>Class</th>\n" to.print "\t\t\t<th style=\"text-align: right;\">Instances</th>\n" to.print "\t\t</tr>\n" to.print "\t</thead>\n" to.print "\t<tbody>\n" constants_m = Module.constants constants_o = Object.constants constants_k = Kernel.constants constants = constants_m + constants_o + constants_k constants.sort.each do |mod| self.write_constant(to, Kernel, mod) end to.print "\t</tbody>\n" to.print "</table>\n" end
Writes information about the garbage-collector to the given IO as HTML.
# File lib/knj/memory_analyzer.rb, line 37 def garbage_collector(to = $stdout) to.print "<h1>Garbage collector</h1>\n" if GC.enable to.print "<div>Garbage collector is not enabled! But it is again now!</div>\n" else to.print "<div>Garbage collector is enabled.</div>\n" end GC.start end
Collects a lot of information and writes a lot of info about the spawned global variables as HTML to the given IO.
# File lib/knj/memory_analyzer.rb, line 168 def global_vars(to = $stdout) to.print "<h1>Global variables</h1>\n" to.print "<table class=\"global_variables list\">\n" to.print "\t<thead>\n" to.print "\t\t<tr>\n" to.print "\t\t\t<th>Name</th>\n" to.print "\t\t</tr>\n" to.print "\t</thead>\n" to.print "\t<tbody>\n" count = 0 Kernel.global_variables.each do |name| count += 1 #begin # global_var_ref = eval(name.to_s) #rescue => e # to.print "\t\t<tr>\n" # to.print "\t\t\t<td>Error: #{Knj::Web.html(e.message)}</td>\n" # to.print "\t\t</tr>\n" # # next #end #size = 0 #size = Knj::Memory_analyzer::Object_size_counter.new(global_var_ref).calculate_size #size = size.to_f / 1024.0 to.print "\t\t<tr>\n" to.print "\t\t\t<td>#{Knj::Web.html(name)}</td>\n" to.print "\t\t</tr>\n" end if count <= 0 to.print "\t\t<tr>\n" to.print "\t\t\t<td colspan=\"2\" class=\"error\">No global variables has been defined.</td>\n" to.print "\t\t</tr>\n" end to.print "\t</tbody>\n" to.print "</table>\n" end
Collects and writes out a lot of information about the spawned hashes as HTML to the given IO.
# File lib/knj/memory_analyzer.rb, line 50 def hashes(to = $stdout) hashes = {} ObjectSpace.each_object(Hash) do |hash| begin keys_orig = hash.keys.sort rescue ArgumentError #When unable to sort regexps... next end keys = [] keys_orig.each do |key| keys << key.to_s end if keys.empty? keystr = :empty else keystr = keys.join(":") end if !hashes.key?(keystr) hashes[keystr] = 1 else hashes[keystr] += 1 end end hashes.delete_if do |key, val| val < 100 end hashes = Knj::ArrayExt.hash_sort(hashes) do |h1, h2| h2[1] <=> h1[1] end to.print "<h1>Hashes</h1>\n" to.write "<table class=\"hashes list\">\n" to.write "\t<thead>\n" to.write "\t\t<tr>\n" to.write "\t\t\t<th>Hash keys</th>\n" to.write "\t\t\t<th>Instances</th>\n" to.write "\t\t</tr>\n" to.write "\t</thead>\n" to.write"\t<tbody>\n" hashes.each do |key, val| to.write "\t\t<tr>\n" to.write "\t\t\t<td>#{Knj::Web.html(key)}</td>\n" to.write "\t\t\t<td>#{Knj::Locales.number_out(val, 0)}</td>\n" to.write "\t\t</tr>\n" end to.write "\t</tbody>\n" to.write "</table>\n" end
Writes information about which files (with line numbers) has spawned the most alive procs. This can give hints about memory leaks.
# File lib/knj/memory_analyzer.rb, line 296 def procs(to = $stdout) procs = {} ObjectSpace.each_object(Proc) do |blk| sloc_arr = blk.source_location next if !sloc_arr sloc = "#{sloc_arr[0]}:#{sloc_arr[1]}" procs[sloc] = 0 if !procs.key?(sloc) procs[sloc] += 1 end procs = Knj::ArrayExt.hash_sort(procs) do |ele1, ele2| ele2[1] <=> ele1[1] end to.puts "<h1>Alive procs by source</h1>" to.puts "<table style=\"width: 600px;\">" to.puts "\t<tbody>" procs.each do |sloc, count| next if count <= 2 to.puts "\t\t<tr>" to.puts "\t\t\t<td>" to.puts "\t\t\t\t#{sloc}" to.puts "\t\t\t</td>" to.puts "\t\t\t<td style=\"text-align: right;\">" to.puts "\t\t\t\t#{_hb.num(count, 0)}" to.puts "\t\t\t</td>" to.puts "\t\t</tr>" end to.puts "\t</tbody>" to.puts "</table>" end
Writes all available memory-analyzer-information to the given IO.
# File lib/knj/memory_analyzer.rb, line 12 def write(to = $stdout) to.print "<div style=\"width: 600px;\">\n" self.garbage_collector(to) GC.start self.arrays(to) GC.start self.hashes(to) GC.start self.constants(to) GC.start self.global_vars(to) GC.start self.procs(to) GC.start to.print "</div>\n" end
Writes information about the given mod and submod to the given IO as HTML.
# File lib/knj/memory_analyzer.rb, line 237 def write_constant(to, mod, submod) submod_s = submod.to_s #return false if mod.name.to_s == "Object" or mod.name.to_s == "Module" return false if @printed.key?(submod_s) return false if mod.autoload?(submod) return false if !mod.const_defined?(submod) @printed[submod_s] = true instances = 0 invalid_submod_size_names = ["BasicObject", "Kernel", "Object", "FALSE"] if invalid_submod_size_names.index(submod_s) != nil size = "-" calc_size = false else size = 0 calc_size = true end classobj = mod.const_get(submod) begin ObjectSpace.each_object(classobj) do |obj| instances += 1 end rescue => e emsg = e.message.to_s if emsg.index("no such file to load") != nil or emsg.index("class or module required") != nil or emsg.index("uninitialized constant") != nil #return false else raise e end end if mod.to_s == "Kernel" or mod.to_s == "Class" or mod.to_s == "Object" mod_title = submod_s else mod_title = "#{mod.to_s}::#{submod_s}" end if instances > 0 to.print "\t\t<tr>\n" to.print "\t\t\t<td>#{mod_title.html}</td>\n" to.print "\t\t\t<td style=\"text-align: right;\">#{Knj::Locales.number_out(instances, 0)}</td>\n" to.print "\t\t</tr>\n" GC.start end if classobj.respond_to?("constants") classobj.constants.sort.each do |subsubmod| self.write_constant(to, classobj, subsubmod) end end end