LeakCanary 2: Leaner, Better, Faster, Kotliner!

06c6a07275076c6b525e2b14018ad78e?s=47 PY
April 22, 2019

LeakCanary 2: Leaner, Better, Faster, Kotliner!

LeakCanary 2 Alpha 1 will be released right before this talk, come learn about hot new features, crazy improvements and gnarly implementation details!

LeakCanary 1 helped detect memory leaks but had several issues: constant freeze of the UI, OOM crashes while analyzing, results hard to analyze, a confusing API, and an ugly logo.

No more! Come learn about multiple leak detection, leak grouping, smarter analysis results, counting leaks in production, detecting leaks in UI tests... and much more!

Oh and LeakCanary 2 is 100% Kotlin, updated to Android X, and it even has a cute new logo!

06c6a07275076c6b525e2b14018ad78e?s=128

PY

April 22, 2019
Tweet

Transcript

  1. 2.
  2. 3.
  3. 4.
  4. 5.
  5. 6.
  6. 7.
  7. 8.
  8. 9.
  9. 10.
  10. 11.
  11. 12.
  12. 13.
  13. 14.
  14. 15.
  15. 16.
  16. 17.
  17. 18.
  18. 20.
  19. 21.

    * Instance of com.squareup.ui.permissions.PasscodeUnlockView | mAccessibilityCursorPosition = -1 | mAccessibilityDelegate

    = null | mAccessibilityTraversalAfterId = -1 | mAccessibilityTraversalBeforeId = -1 | mAccessibilityViewId = -1 | mAnimator = null | mAttachInfo = null | mAttributes = null | mBackground = android.graphics.drawable.ColorDrawable@328049760 (0x138da | mBackgroundRenderNode = android.view.RenderNode@338685248 (0x142fed40) | mBackgroundResource = 0 | mBackgroundSizeChanged = false | mBackgroundTint = null | mBottom = 1731 | mCachingFailed = false | mClipBounds = null @Piwai
  20. 22.
  21. 23.

    class ActivityInspector : Reachability.Inspector { override fun expectedReachability(element: LeakTraceElement): Reachability

    { val fieldValue = element.getFieldReferenceValue("mDestroyed") if (fieldValue == null) { return Reachability.unknown() } return if (fieldValue == "true") { Reachability.unreachable("Activity#mDestroyed is true") } else { Reachability.reachable("Activity#mDestroyed is false") } } } @Piwai
  22. 24.
  23. 25.
  24. 26.
  25. 27.
  26. 28.
  27. 29.

    private fun createGroupHash(): String { val uniqueString = if (excludedLeak)

    { leakTrace.firstElementExclusion.matching } else { leakTrace.leakCauses .joinToString(separator = "") { element -> val referenceName = element.reference!!.groupingName element.className + referenceName } } return uniqueString.createSHA1Hash() } @Piwai
  28. 30.
  29. 31.
  30. 32.
  31. 33.
  32. 34.
  33. 35.
  34. 36.
  35. 38.

    fun open(heapDump: File): HprofParser { val inputStream = heapDump.inputStream() val

    channel = inputStream.channel val source = inputStream.source() .buffer() // ... } @Piwai
  36. 39.

    fun open(heapDump: File): HprofParser { val inputStream = heapDump.inputStream() val

    channel = inputStream.channel val source = inputStream.source() .buffer() // ... } fun moveTo(newPosition: Long) { if (position == newPosition) { return } source.buffer.clear() channel.position(newPosition) position = newPosition } @Piwai
  37. 40.
  38. 41.
  39. 42.
  40. 43.
  41. 44.

    while (!exhausted()) { // type of the record val tag

    = readUnsignedByte() // number of microseconds since the time stamp in the header skip(INT_SIZE) // number of bytes that follow and belong to this record val length = readUnsignedInt() when (tag) { STRING_IN_UTF8 -> { val callback = callbacks.get<StringRecord>() val id = readId() val stringLength = length - idSize val string = readUtf8(stringLength) callback(StringRecord(id, string)) } // ... @Piwai
  42. 45.
  43. 46.

    // ... LOAD_CLASS -> { val classSerialNumber = readInt() val

    id = readId() val stackTraceSerialNumber = readInt() val classNameStringId = readId() @Piwai
  44. 47.
  45. 48.
  46. 49.
  47. 50.
  48. 51.
  49. 52.
  50. 53.
  51. 54.
  52. 55.
  53. 56.
  54. 57.
  55. 58.
  56. 59.
  57. 60.