ObjectInspectors.kt
TLDR
This file contains an enumeration class ObjectInspectors
that implements the ObjectInspector
interface. It provides a set of default object inspectors that are used to inspect common JDK objects and identify any potential memory leaks.
Methods
This file does not contain any methods.
Classes
Class: ObjectInspectors
This enumeration class implements the ObjectInspector
interface and provides a set of default object inspectors that are used to inspect common JDK objects. Each object inspector is defined as a constant in this enumeration.
Enum Constant: KEYED_WEAK_REFERENCE
This object inspector is used to inspect objects that are referenced by KeyedWeakReference
instances. It checks if the object is still referenced and retained by any KeyedWeakReference
in the heap graph.
Enum Constant: CLASSLOADER
This object inspector is used to inspect instances of the ClassLoader
class. It adds a not leaking reason stating that a ClassLoader
is never leaking.
Enum Constant: CLASS
This object inspector is used to inspect instances of the HeapClass
class. It adds a not leaking reason stating that a class is never leaking.
Enum Constant: ANONYMOUS_CLASS
This object inspector is used to inspect instances of anonymous classes. It identifies anonymous classes by checking if their names match the regular expression pattern ^.+\\$\\d+$
. If an instance is identified as an anonymous class, it adds labels indicating its parent class or implemented interface.
Enum Constant: THREAD
This object inspector is used to inspect instances of the Thread
class. It retrieves the thread name and adds it as a label.
Property: leakingObjectFilter
This property is an optional lambda that defines a custom filter to determine if an object is leaking.
Property: jdkDefaults
This property returns a list of the default object inspectors provided by this enumeration.
Property: jdkLeakingObjectFilters
This property returns a list of LeakingObjectFilter
based on the default object inspectors.
Method: createLeakingObjectFilters(inspectors: Set<ObjectInspectors>): List<LeakingObjectFilter>
This method creates a list of LeakingObjectFilter
based on the provided set of ObjectInspectors
.
/*
* Copyright (C) 2018 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package shark
import shark.FilteringLeakingObjectFinder.LeakingObjectFilter
import shark.HeapObject.HeapClass
import shark.HeapObject.HeapInstance
import java.util.EnumSet
/**
* A set of default [ObjectInspector]s that knows about common JDK objects.
*/
enum class ObjectInspectors : ObjectInspector {
KEYED_WEAK_REFERENCE {
override val leakingObjectFilter = { heapObject: HeapObject ->
KeyedWeakReferenceFinder.findKeyedWeakReferences(heapObject.graph)
.filter { it.hasReferent && it.isRetained }
.any { reference ->
reference.referent.value == heapObject.objectId
}
}
override fun inspect(
reporter: ObjectReporter
) {
val graph = reporter.heapObject.graph
val references = KeyedWeakReferenceFinder.findKeyedWeakReferences(graph)
val objectId = reporter.heapObject.objectId
references.forEach { ref ->
if (ref.referent.value == objectId) {
reporter.leakingReasons += if (ref.description.isNotEmpty()) {
"ObjectWatcher was watching this because ${ref.description}"
} else {
"ObjectWatcher was watching this"
}
reporter.labels += "key = ${ref.key}"
if (ref.watchDurationMillis != null) {
reporter.labels += "watchDurationMillis = ${ref.watchDurationMillis}"
}
if (ref.retainedDurationMillis != null) {
reporter.labels += "retainedDurationMillis = ${ref.retainedDurationMillis}"
}
}
}
}
},
CLASSLOADER {
override fun inspect(
reporter: ObjectReporter
) {
reporter.whenInstanceOf(ClassLoader::class) {
notLeakingReasons += "A ClassLoader is never leaking"
}
}
},
CLASS {
override fun inspect(
reporter: ObjectReporter
) {
if (reporter.heapObject is HeapClass) {
reporter.notLeakingReasons += "a class is never leaking"
}
}
},
ANONYMOUS_CLASS {
override fun inspect(
reporter: ObjectReporter
) {
val heapObject = reporter.heapObject
if (heapObject is HeapInstance) {
val instanceClass = heapObject.instanceClass
if (instanceClass.name.matches(ANONYMOUS_CLASS_NAME_PATTERN_REGEX)) {
val parentClassRecord = instanceClass.superclass!!
if (parentClassRecord.name == "java.lang.Object") {
try {
// This is an anonymous class implementing an interface. The API does not give access
// to the interfaces implemented by the class. We check if it's in the class path and
// use that instead.
val actualClass = Class.forName(instanceClass.name)
val interfaces = actualClass.interfaces
reporter.labels += if (interfaces.isNotEmpty()) {
val implementedInterface = interfaces[0]
"Anonymous class implementing ${implementedInterface.name}"
} else {
"Anonymous subclass of java.lang.Object"
}
} catch (ignored: ClassNotFoundException) {
}
} else {
// Makes it easier to figure out which anonymous class we're looking at.
reporter.labels += "Anonymous subclass of ${parentClassRecord.name}"
}
}
}
}
},
THREAD {
override fun inspect(
reporter: ObjectReporter
) {
reporter.whenInstanceOf(Thread::class) { instance ->
val threadName = instance[Thread::class, "name"]!!.value.readAsJavaString()
labels += "Thread name: '$threadName'"
}
}
};
internal open val leakingObjectFilter: ((heapObject: HeapObject) -> Boolean)? = null
companion object {
private const val ANONYMOUS_CLASS_NAME_PATTERN = "^.+\\$\\d+$"
private val ANONYMOUS_CLASS_NAME_PATTERN_REGEX = ANONYMOUS_CLASS_NAME_PATTERN.toRegex()
/** @see ObjectInspectors */
val jdkDefaults: List<ObjectInspector>
get() {
return values().toList()
}
/**
* Returns a list of [LeakingObjectFilter] suitable for common JDK projects.
*/
val jdkLeakingObjectFilters: List<LeakingObjectFilter> =
createLeakingObjectFilters(EnumSet.allOf(ObjectInspectors::class.java))
/**
* Creates a list of [LeakingObjectFilter] based on the passed in [ObjectInspectors].
*/
fun createLeakingObjectFilters(inspectors: Set<ObjectInspectors>): List<LeakingObjectFilter> =
inspectors.mapNotNull { it.leakingObjectFilter }
.map { filter ->
LeakingObjectFilter { heapObject -> filter(heapObject) }
}
}
}