MemorySourceMemoryUsage

class Usage

Definitions

attr_accessor :size

attr_accessor :count

def [](key)

Access usage attributes by key.

Signature

parameter key Symbol

The attribute name (:size, :count, :memory, :memsize).

Implementation

def [](key)
	public_send(key)
end

def <<(allocation)

Add an allocation to this usage.

Signature

parameter allocation Allocation

The allocation to add.

Implementation

def << allocation
	self.size += allocation.memsize
	self.count += 1
	
	return self
end

def add!(other)

Add another usage to this usage.

Signature

parameter other Usage

The usage to add.

Implementation

def add!(other)
	self.size += other.size
	self.count += other.count
	
	return self
end

def self.of(root, seen: Set.new.compare_by_identity, ignore: IGNORE)

Compute the usage of an object and all reachable objects from it.

The root is always visited even if it is in seen.

Signature

parameter root Object

The root object to start traversal from.

parameter seen Hash(Object, Integer)

The seen objects (should be compare_by_identity).

returns Usage

The usage of the object and all reachable objects from it.

Implementation

def self.of(root, seen: Set.new.compare_by_identity, ignore: IGNORE)
	count = 0
	size = 0
	
	queue = [root]
	while object = queue.shift
		# Add the object to the seen set:
		seen.add(object)
		
		# Update the count and size:
		count += 1
		size += ObjectSpace.memsize_of(object)
		
		# Add the object's reachable objects to the queue:
		ObjectSpace.reachable_objects_from(object)&.each do |reachable_object|
			# Skip ignored types:
			next if ignore.any?{|type| reachable_object.is_a?(type)}
			
			# Skip internal objects - they don't behave correctly when added to `seen` and create unbounded recursion:
			next if reachable_object.is_a?(ObjectSpace::InternalObjectWrapper)
			
			# Skip objects we have already seen:
			next if seen.include?(reachable_object)
			
			queue << reachable_object
		end
	end
	
	return new(size, count)
end