class Memory::Sampler

Sample memory allocations.

~~~ ruby sampler = Sampler.capture do

5.times { "foo" }

end ~~~

Attributes

allocated[R]
cache[R]
filter[R]
wrapper[R]

Public Class Methods

new(&filter) click to toggle source
# File lib/memory/sampler.rb, line 69
def initialize(&filter)
        @filter = filter
        
        @cache = Cache.new
        @wrapper = Wrapper.new(@cache)
        @allocated = Array.new
end

Public Instance Methods

dump(io = nil) click to toggle source
# File lib/memory/sampler.rb, line 111
def dump(io = nil)
        Console.logger.debug(self, "Dumping allocations: #{@allocated.size}")
        
        if io
                packer = @wrapper.packer(io)
                packer.pack(@allocated)
                packer.flush
        else
                @wrapper.dump(@allocated)
        end
end
load(data) click to toggle source
# File lib/memory/sampler.rb, line 123
def load(data)
        allocations = @wrapper.load(data)
        
        Console.logger.debug(self, "Loading allocations: #{allocations.size}")
        
        @allocated.concat(allocations)
end
report() click to toggle source
# File lib/memory/sampler.rb, line 131
def report
        report = Report.general
        
        report.concat(@allocated)
        
        return report
end
run() { |&& false| ... } click to toggle source

Collects object allocation and memory of ruby code inside of passed block.

# File lib/memory/sampler.rb, line 140
def run(&block)
        start
        
        begin
                # We do this to avoid retaining the result of the block.
                yield && false
        ensure
                stop
        end
end
start() click to toggle source
# File lib/memory/sampler.rb, line 83
def start
        GC.disable
        GC.start
        
        @generation = GC.count
        ObjectSpace.trace_object_allocations_start
end
stop() click to toggle source
# File lib/memory/sampler.rb, line 91
def stop
        ObjectSpace.trace_object_allocations_stop
        allocated = track_allocations(@generation)
        
        # **WARNING** Do not allocate any new Objects between the call to GC.start and the completion of the retained lookups. It is likely that a new Object would reuse an object_id from a GC'd object.
        
        GC.enable
        3.times{GC.start}
        
        ObjectSpace.each_object do |object|
                next unless ObjectSpace.allocation_generation(object) == @generation
                
                if found = allocated[object.__id__]
                        found.retained = true
                end
        end
        
        ObjectSpace.trace_object_allocations_clear
end

Private Instance Methods

track_allocations(generation) click to toggle source

Iterates through objects in memory of a given generation. Stores results along with meta data of objects collected.

# File lib/memory/sampler.rb, line 155
def track_allocations(generation)
        rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
        
        allocated = Hash.new.compare_by_identity
        
        ObjectSpace.each_object do |object|
                next unless ObjectSpace.allocation_generation(object) == generation
                
                file = ObjectSpace.allocation_sourcefile(object) || "(no name)"
                
                klass = object.class rescue nil
                
                unless Class === klass
                        # attempt to determine the true Class when .class returns something other than a Class
                        klass = Kernel.instance_method(:class).bind(object).call
                end
                
                next if @filter && !@filter.call(klass, file)
                
                line = ObjectSpace.allocation_sourceline(object)
                
                # we do memsize first to avoid freezing as a side effect and shifting
                # storage to the new frozen string, this happens on @hash[s] in lookup_string
                memsize = ObjectSpace.memsize_of(object)
                class_name = @cache.lookup_class_name(klass)
                value = (klass == String) ? @cache.lookup_string(object) : nil
                
                # compensate for API bug
                memsize = rvalue_size if memsize > 100_000_000_000
                
                allocation = Allocation.new(@cache, class_name, file, line, memsize, value, false)
                
                @allocated << allocation
                allocated[object.__id__] = allocation
        end
        
        return allocated
end