1 /* <lambda>null2 * Copyright (C) 2018 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package shark 17 18 import shark.FilteringLeakingObjectFinder.LeakingObjectFilter 19 import shark.HeapObject.HeapClass 20 import shark.HeapObject.HeapInstance 21 import java.util.EnumSet 22 23 /** 24 * A set of default [ObjectInspector]s that knows about common JDK objects. 25 */ 26 enum class ObjectInspectors : ObjectInspector { 27 28 KEYED_WEAK_REFERENCE { 29 30 override val leakingObjectFilter = { heapObject: HeapObject -> 31 KeyedWeakReferenceFinder.findKeyedWeakReferences(heapObject.graph) 32 .filter { it.hasReferent && it.isRetained } 33 .any { reference -> 34 reference.referent.value == heapObject.objectId 35 } 36 } 37 38 override fun inspect( 39 reporter: ObjectReporter 40 ) { 41 val graph = reporter.heapObject.graph 42 val references = KeyedWeakReferenceFinder.findKeyedWeakReferences(graph) 43 44 val objectId = reporter.heapObject.objectId 45 references.forEach { ref -> 46 if (ref.referent.value == objectId) { 47 reporter.leakingReasons += if (ref.description.isNotEmpty()) { 48 "ObjectWatcher was watching this because ${ref.description}" 49 } else { 50 "ObjectWatcher was watching this" 51 } 52 reporter.labels += "key = ${ref.key}" 53 if (ref.watchDurationMillis != null) { 54 reporter.labels += "watchDurationMillis = ${ref.watchDurationMillis}" 55 } 56 if (ref.retainedDurationMillis != null) { 57 reporter.labels += "retainedDurationMillis = ${ref.retainedDurationMillis}" 58 } 59 } 60 } 61 } 62 }, 63 64 CLASSLOADER { 65 override fun inspect( 66 reporter: ObjectReporter 67 ) { 68 reporter.whenInstanceOf(ClassLoader::class) { 69 notLeakingReasons += "A ClassLoader is never leaking" 70 } 71 } 72 }, 73 74 CLASS { 75 override fun inspect( 76 reporter: ObjectReporter 77 ) { 78 if (reporter.heapObject is HeapClass) { 79 reporter.notLeakingReasons += "a class is never leaking" 80 } 81 } 82 }, 83 84 ANONYMOUS_CLASS { 85 override fun inspect( 86 reporter: ObjectReporter 87 ) { 88 val heapObject = reporter.heapObject 89 if (heapObject is HeapInstance) { 90 val instanceClass = heapObject.instanceClass 91 if (instanceClass.name.matches(ANONYMOUS_CLASS_NAME_PATTERN_REGEX)) { 92 val parentClassRecord = instanceClass.superclass!! 93 if (parentClassRecord.name == "java.lang.Object") { 94 try { 95 // This is an anonymous class implementing an interface. The API does not give access 96 // to the interfaces implemented by the class. We check if it's in the class path and 97 // use that instead. 98 val actualClass = Class.forName(instanceClass.name) 99 val interfaces = actualClass.interfaces 100 reporter.labels += if (interfaces.isNotEmpty()) { 101 val implementedInterface = interfaces[0] 102 "Anonymous class implementing ${implementedInterface.name}" 103 } else { 104 "Anonymous subclass of java.lang.Object" 105 } 106 } catch (ignored: ClassNotFoundException) { 107 } 108 } else { 109 // Makes it easier to figure out which anonymous class we're looking at. 110 reporter.labels += "Anonymous subclass of ${parentClassRecord.name}" 111 } 112 } 113 } 114 } 115 }, 116 117 THREAD { 118 override fun inspect( 119 reporter: ObjectReporter 120 ) { 121 reporter.whenInstanceOf(Thread::class) { instance -> 122 val threadName = instance[Thread::class, "name"]!!.value.readAsJavaString() 123 labels += "Thread name: '$threadName'" 124 } 125 } 126 }; 127 128 internal open val leakingObjectFilter: ((heapObject: HeapObject) -> Boolean)? = null 129 130 companion object { 131 private const val ANONYMOUS_CLASS_NAME_PATTERN = "^.+\\$\\d+$" 132 private val ANONYMOUS_CLASS_NAME_PATTERN_REGEX = ANONYMOUS_CLASS_NAME_PATTERN.toRegex() 133 134 /** @see ObjectInspectors */ 135 val jdkDefaults: List<ObjectInspector> 136 get() { 137 return values().toList() 138 } 139 140 /** 141 * Returns a list of [LeakingObjectFilter] suitable for common JDK projects. 142 */ 143 val jdkLeakingObjectFilters: List<LeakingObjectFilter> = 144 createLeakingObjectFilters(EnumSet.allOf(ObjectInspectors::class.java)) 145 146 /** 147 * Creates a list of [LeakingObjectFilter] based on the passed in [ObjectInspectors]. 148 */ 149 fun createLeakingObjectFilters(inspectors: Set<ObjectInspectors>): List<LeakingObjectFilter> = 150 inspectors.mapNotNull { it.leakingObjectFilter } 151 .map { filter -> 152 LeakingObjectFilter { heapObject -> filter(heapObject) } 153 } 154 } 155 } 156