ChainingInstanceReferenceReader.kt
TLDR
The ChainingInstanceReferenceReader.kt
file is a Kotlin file that defines the ChainingInstanceReferenceReader
class. It is a reference reader that first delegates expanding to a list of virtual reference readers until one matches or none, and then proceeds with a field reference reader. It allows for correct tracking of visited objects and proper computation of dominators and retained size.
Classes
ChainingInstanceReferenceReader
The ChainingInstanceReferenceReader
class is a reference reader that reads references from a heap instance. It takes a list of virtual reference readers and a field reference reader as parameters. It implements the ReferenceReader
interface for HeapInstance
.
Interface: ChainingInstanceReferenceReader.VirtualInstanceReferenceReader
The VirtualInstanceReferenceReader
interface extends the ReferenceReader
for HeapInstance
. It defines two methods:
-
matches(instance: HeapInstance)
: This method determines whether this virtual instance reference reader can expand the provided heap instance. -
read(instance: HeapInstance)
: This method reads references from the provided heap instance.
Interface: ChainingInstanceReferenceReader.VirtualInstanceReferenceReader.OptionalFactory
The OptionalFactory
interface defines a method create(graph: HeapGraph)
that creates a new VirtualInstanceReferenceReader
based on the classes in the heap graph. It may return a different ReferenceReader
implementation depending on the version of a class present in the heap dump, or it may return null if the class is missing.
Interface: ChainingInstanceReferenceReader.VirtualInstanceReferenceReader.ChainFactory
The ChainFactory
interface defines a method createFor(graph: HeapGraph)
that creates a list of VirtualInstanceReferenceReader
s based on the classes in the heap graph and their implementation. The list elements will process references in order in the ChainingInstanceReferenceReader
.
package shark
import shark.HeapObject.HeapInstance
/**
* A [ReferenceReader] that first delegates expanding to [virtualRefReaders] in order until one
* matches (or none), and then always proceeds with [fieldRefReader]. This means any
* synthetic ref will be on the shortest path, but we still explore the entire data structure so
* that we correctly track which objects have been visited and correctly compute dominators and
* retained size.
*/
class ChainingInstanceReferenceReader(
private val virtualRefReaders: List<VirtualInstanceReferenceReader>,
private val fieldRefReader: FieldInstanceReferenceReader
) : ReferenceReader<HeapInstance> {
override fun read(source: HeapInstance): Sequence<Reference> {
val virtualRefs = expandVirtualRefs(source)
// Note: always forwarding to fieldRefReader means we may navigate the structure twice
// which increases IO reads. However this is a trade-of that allows virtualRef impls to
// focus on a subset of references and more importantly it means we still get a proper
// calculation of retained size as we don't skip any instance.
val fieldRefs = fieldRefReader.read(source)
return virtualRefs + fieldRefs
}
private fun expandVirtualRefs(instance: HeapInstance): Sequence<Reference> {
for (expander in virtualRefReaders) {
if (expander.matches(instance)) {
return expander.read(instance)
}
}
return emptySequence()
}
/**
* Same as [ReferenceReader] but [read] is only invoked when [matches] returns
* true. [matches] should return false if this [VirtualInstanceReferenceReader] implementation isn't
* able to expand the provided instance, in which case [ChainingInstanceReferenceReader] will delegate
* to the next [VirtualInstanceReferenceReader] implementation.
*/
interface VirtualInstanceReferenceReader : ReferenceReader<HeapInstance> {
fun matches(instance: HeapInstance): Boolean
/**
* May create a new [VirtualInstanceReferenceReader], depending on what's in the heap graph.
* [OptionalFactory] implementations might return a different [ReferenceReader]
* depending on which version of a class is present in the heap dump, or they might return null if
* that class is missing.
*/
fun interface OptionalFactory {
fun create(graph: HeapGraph): VirtualInstanceReferenceReader?
}
/**
* Creates a list of [VirtualInstanceReferenceReader] where the content of the list depends on
* the classes in the heap graph and their implementation. This is a chain as
* [VirtualInstanceReferenceReader] elements in the list will process references in order in
* [ChainingInstanceReferenceReader].
*/
fun interface ChainFactory {
fun createFor(graph: HeapGraph): List<VirtualInstanceReferenceReader>
}
}
}