ClassReferenceReader.kt
TLDR
This file contains the implementation of the ClassReferenceReader
class, which is responsible for reading references to heap objects of type HeapClass
. It takes a HeapGraph
and a list of ReferenceMatcher
objects as input.
Classes
ClassReferenceReader
The ClassReferenceReader
class is responsible for reading references to HeapClass
objects in the heap graph. It implements the ReferenceReader
interface and takes a HeapGraph
and a list of ReferenceMatcher
objects as input. The class provides a read
method that takes a HeapClass
object as input and returns a sequence of Reference
objects representing the references to the static fields of the given HeapClass
. The class also has an init
block that initializes a mapping of static field names to reference matchers based on the provided referenceMatchers
.
package shark
import shark.HeapObject.HeapClass
import shark.Reference.LazyDetails
import shark.ReferenceLocationType.STATIC_FIELD
import shark.ReferencePattern.StaticFieldPattern
import shark.ValueHolder.ReferenceHolder
class ClassReferenceReader(
graph: HeapGraph,
referenceMatchers: List<ReferenceMatcher>
) : ReferenceReader<HeapClass> {
private val staticFieldNameByClassName: Map<String, Map<String, ReferenceMatcher>>
init {
val staticFieldNameByClassName = mutableMapOf<String, MutableMap<String, ReferenceMatcher>>()
referenceMatchers.filterFor(graph).forEach { referenceMatcher ->
val pattern = referenceMatcher.pattern
if (pattern is StaticFieldPattern) {
val mapOrNull = staticFieldNameByClassName[pattern.className]
val map = if (mapOrNull != null) mapOrNull else {
val newMap = mutableMapOf<String, ReferenceMatcher>()
staticFieldNameByClassName[pattern.className] = newMap
newMap
}
map[pattern.fieldName] = referenceMatcher
}
}
this.staticFieldNameByClassName = staticFieldNameByClassName
}
override fun read(source: HeapClass): Sequence<Reference> {
val ignoredStaticFields = staticFieldNameByClassName[source.name] ?: emptyMap()
return source.readStaticFields().mapNotNull { staticField ->
// not non null: no null + no primitives.
if (!staticField.value.isNonNullReference) {
return@mapNotNull null
}
val fieldName = staticField.name
if (fieldName == "\$staticOverhead" || fieldName == "\$classOverhead") {
return@mapNotNull null
}
// Note: instead of calling staticField.value.asObjectId!! we cast holder to ReferenceHolder
// and access value directly. This allows us to avoid unnecessary boxing of Long.
val valueObjectId = (staticField.value.holder as ReferenceHolder).value
val referenceMatcher = ignoredStaticFields[fieldName]
if (referenceMatcher is IgnoredReferenceMatcher) {
null
} else {
val sourceObjectId = source.objectId
Reference(
valueObjectId = valueObjectId,
isLowPriority = referenceMatcher != null,
lazyDetailsResolver = {
LazyDetails(
name = fieldName,
locationClassObjectId = sourceObjectId,
locationType = STATIC_FIELD,
isVirtual = false,
matchedLibraryLeak = referenceMatcher as LibraryLeakReferenceMatcher?,
)
}
)
}
}
}
}