1 package shark.internal 2 3 import shark.HeapGraph 4 import shark.HeapObject.HeapClass 5 import shark.HeapObject.HeapInstance 6 import shark.HeapObject.HeapObjectArray 7 import shark.HeapObject.HeapPrimitiveArray 8 import shark.internal.ObjectArrayReferenceReader.Companion.isSkippablePrimitiveWrapperArray 9 import shark.ValueHolder 10 11 /** 12 * Provides approximations for the shallow size of objects in memory. 13 * 14 * Determining the actual shallow size of an object in memory is hard, as it changes for each VM 15 * implementation, depending on the various memory layout optimizations and bit alignment. 16 * 17 * More on this topic: https://dev.to/pyricau/the-real-size-of-android-objects-1i2e 18 */ 19 internal class ShallowSizeCalculator(private val graph: HeapGraph) { 20 computeShallowSizenull21 fun computeShallowSize(objectId: Long): Int { 22 return when (val heapObject = graph.findObjectById(objectId)) { 23 is HeapInstance -> { 24 if (heapObject.instanceClassName == "java.lang.String") { 25 // In PathFinder we ignore the value field of String instances when building the dominator 26 // tree, so we add that size back here. 27 val valueObjectId = 28 heapObject["java.lang.String", "value"]?.value?.asNonNullObjectId 29 heapObject.byteSize + if (valueObjectId != null) { 30 computeShallowSize(valueObjectId) 31 } else { 32 0 33 } 34 } else { 35 // Total byte size of fields for instances of this class, as registered in the class dump. 36 // The actual memory layout likely differs. 37 heapObject.byteSize 38 } 39 } 40 // Number of elements * object id size 41 is HeapObjectArray -> { 42 if (heapObject.isSkippablePrimitiveWrapperArray) { 43 // In PathFinder we ignore references from primitive wrapper arrays when building the 44 // dominator tree, so we add that size back here. 45 val elementIds = heapObject.readRecord().elementIds 46 val shallowSize = elementIds.size * graph.identifierByteSize 47 val firstNonNullElement = elementIds.firstOrNull { it != ValueHolder.NULL_REFERENCE } 48 if (firstNonNullElement != null) { 49 val sizeOfOneElement = computeShallowSize(firstNonNullElement) 50 val countOfNonNullElements = elementIds.count { it != ValueHolder.NULL_REFERENCE } 51 shallowSize + (sizeOfOneElement * countOfNonNullElements) 52 } else { 53 shallowSize 54 } 55 } else { 56 heapObject.byteSize 57 } 58 } 59 // Number of elements * primitive type size 60 is HeapPrimitiveArray -> heapObject.byteSize 61 // This is probably way off but is a cheap approximation. 62 is HeapClass -> heapObject.recordSize 63 } 64 } 65 } 66