Repository: square/leakcanary
File: shark-graph/src/main/java/shark/HprofHeapGraph.kt (Kotlin)

Code before:
      val indexedObject = it.second
      HeapPrimitiveArray(this, indexedObject, objectId)
    }

  // LRU cache size of 3000 is a sweet spot to balance hits vs memory usage.
  // This is based on running InstrumentationLeakDetectorTest a bunch of time on a
  // Pixel 2 XL API 28. Hit count was ~120K, miss count ~290K
  private val objectCache = LruCache<Long, ObjectRecord>(3000)

  // java.lang.Object is the most accessed class in Heap, so we want to memorize a reference to it
  private val javaLangObject: HeapClass

  init {
    val classId = index.classId("java.lang.Object")!!
    val indexedObject = index.indexedObjectOrNull(classId)!!
    javaLangObject = wrapIndexedObject(indexedObject, classId) as HeapClass
  }

  override fun findObjectById(objectId: Long): HeapObject {
    return findObjectByIdOrNull(objectId) ?: throw IllegalArgumentException(
        "Object id $objectId not found in heap dump."
    )
  }

  override fun findObjectByIdOrNull(objectId: Long): HeapObject? {
    if (objectId == javaLangObject.objectId) return javaLangObject

    val indexedObject = index.indexedObjectOrNull(objectId) ?: return null
    return wrapIndexedObject(indexedObject, objectId)
  }

  override fun findClassByName(className: String): HeapClass? {
    val classId = index.classId(className)
    return if (classId == null) {
      null
    } else {
      return findObjectById(classId) as HeapClass
    }
  }

  override fun objectExists(objectId: Long): Boolean {
    return index.objectIdIsIndexed(objectId)
  }

  internal fun fieldName(
    classId: Long,
    fieldRecord: FieldRecord
  ): String {
    return index.fieldName(classId, fieldRecord.nameStringId)
  }


Review comment (performance):
Side note: we already have an LRU cache in front of operations that requires IO. However separately we also try to rely on the indexed objects data as much as possible to avoid as much IO as we can. What this PR does is skipping a ton of binary searches into the indexed objects when we look for the `java.lang.Object` class again and again (e.g. when going through class hierarchy for every class).

We could also add a small LruCache at this layer at some point. We'd have to evaluate the cost / benefit of adding a hash lookup to every `findObjectByIdOrNull` to avoid a binary search for a subset.

Code after:
      val indexedObject = it.second
      HeapPrimitiveArray(this, indexedObject, objectId)
    }

  // LRU cache size of 3000 is a sweet spot to balance hits vs memory usage.
  // This is based on running InstrumentationLeakDetectorTest a bunch of time on a
  // Pixel 2 XL API 28. Hit count was ~120K, miss count ~290K
  private val objectCache = LruCache<Long, ObjectRecord>(3000)

  // java.lang.Object is the most accessed class in Heap, so we want to memoize a reference to it
  private val javaLangObjectClass: HeapClass? = findClassByName("java.lang.Object")

  override fun findObjectById(objectId: Long): HeapObject {
    return findObjectByIdOrNull(objectId) ?: throw IllegalArgumentException(
        "Object id $objectId not found in heap dump."
    )
  }

  override fun findObjectByIdOrNull(objectId: Long): HeapObject? {
    if (objectId == javaLangObjectClass?.objectId) return javaLangObjectClass

    val indexedObject = index.indexedObjectOrNull(objectId) ?: return null
    return wrapIndexedObject(indexedObject, objectId)
  }

  override fun findClassByName(className: String): HeapClass? {
    val classId = index.classId(className)
    return if (classId == null) {
      null
    } else {
      return findObjectById(classId) as HeapClass
    }
  }

  override fun objectExists(objectId: Long): Boolean {
    return index.objectIdIsIndexed(objectId)
  }

  internal fun fieldName(
    classId: Long,
    fieldRecord: FieldRecord
  ): String {
    return index.fieldName(classId, fieldRecord.nameStringId)
  }

  internal fun staticFieldName(
    classId: Long,
    fieldRecord: StaticFieldRecord
  ): String {
    return index.fieldName(classId, fieldRecord.nameStringId)
  }