Android性能优化之内存泄漏优化(工具篇)

打印 上一主题 下一主题

主题 522|帖子 522|积分 1566

Android 内存泄漏指的是应用程序在运行过程中,因为一些原因导致不再使用的对象无法被垃圾回收器回收,从而使得这些对象占用的内存无法释放,终极导致内存占用逐渐增加,大概导致应用程序的性能下降、响应变慢甚至瓦解。
一、Android Memory Profiler

官方文档:https://developer.android.com/studio/profile/memory-profiler
内存性能分析器是 Android Profiler 中的一个组件,可帮助您识别大概会导致应用卡顿、冻结甚至瓦解的内存泄漏和内存抖动。它表现一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。

如需打开内存分析器,请按以下步骤利用:

  • 依次点击 View > Tool Windows > Profiler(您也可以点击工具栏中的 Profile 图标 )。
  • 从 Android Profiler 工具栏中选择要分析的设备和应用进程。假如您已通过 USB 连接设备但系统未列出该设备,请确保您已启用 USB 调试。
  • 点击 MEMORY 时间轴上的任意位置以打开内存分析器。
    或者,您可以从命令利用用 dumpsys 检查您的应用内存,还可以在 logcat 中查看 GC 事件。
假如存在内存泄漏,应用在转到后台运行时,仍大概保留相应内存。此行为会导致系统强制执行不须要的垃圾回收事件,因而拖慢系统别的部分的内存性能。终极,系统将被迫终止您的应用进程以回收内存。然后,当用户返回您的应用时,它必须完全重启。
为帮助防止这些题目,您应使用内存分析器执行以下利用:

  • 在时间轴上查找大概会导致性能题目的不理想的内存分配模式。
  • 转储 Java 堆以查看在任何给定时间有哪些对象在占用内存。在一个较长的时间段内进行多次堆转储有助于识别内存泄漏。
  • 纪录正常条件和极度条件下用户交互期间的内存分配情况,从而准确识别您的代码是否在短时间内分配了过多对象,或所分配的对象是否出现了泄漏。

捕获堆转储
堆转储表现在您捕获堆转储时您的应用中哪些对象正在使用内存。特别是在长时间的用户会话后,堆转储会表现您认为不应再位于内存中却仍在内存中的对象,从而帮助识别内存泄漏。
捕获堆转储后,您可以查看以下信息:


  • 您的应用分配了哪些类型的对象,以及每种对象有多少。
  • 每个对象当前使用多少内存。
  • 在代码中的什么位置保持着对每个对象的引用。
  • 对象所分配到的调用堆栈。(现在,对于 Android 7.1 及更低版本,只有在纪录分配期间捕获堆转储时,才会表现调用堆栈的堆转储。)
如需捕获堆转储,请点击 Capture heap dump,然后选择 Record。在转储堆期间,Java 内存量大概会暂时增加。 这很正常,因为堆转储与您的应用发生在同一进程中,并需要一些内存以网络数据。
在分析器捕获堆转储后,内存分析器界面将转换到表现堆转储的单独屏幕。

如需检查您的堆,请按以下步骤利用:


  • 欣赏列表以查找堆计数异常大且大概存在泄漏的对象。为帮助查找已知类,点击 Class Name 列标题以按字母顺序排序。然后,点击一个类名称。此时右侧将出现 Instance View 窗格,表现该类的每个实例。
    别的,您也可以快速找到对象,方法是点击 Filter 图标 ,或按 Ctrl+F 键(在 Mac 上,按 Command+F 键),然后在搜索字段中输入类或软件包名称。假如从下拉菜单中选择 Arrange by callstack,还可以按方法名称搜索。如需使用正则表达式,请勾选 Regex 旁边的复选框。假如您的搜索查询区分巨细写,请勾选 Match case 旁边的复选框。
  • 在 Instance View 窗格中,点击一个实例。此时下方将出现 References 标签页,表现对该对象的每个引用。
    或者,点击实例名称旁边的箭头以查看其所有字段,然后点击一个字段名称以查看其所有引用。如需查看某个字段的实例详细信息,请右键点击该字段并选择 Go to Instance。
  • 在 References 标签页中,假如您发现某个引用大概在泄漏内存,请右键点击它并选择 Go to Instance。如许会从堆转储中选择相应的实例,从而向您表现它自己的实例数据。

在您的堆转储中,请注意由下列任意情况引起的内存泄漏:


  • 长时间引用 Activity、Context、View、Drawable 和其他对象,大概会保持对 Activity 或 Context 容器的引用。
  • 可以保持 Activity 实例的非静态内部类,如 Runnable。
  • 对象保持时间比所需时间长的缓存。
二、Memory Analyzer (MAT)

官网:https://eclipse.dev/mat/


  • 概述(Overview)
    获取堆转储的概览:顶部是对象的巨细和总数,然后是包罗最大对象和链接的饼图,以便继续分析。

  • 直方图(Histogram)
    直方图列出了按种别分组的对象。内存分析器可以非常快速地估算出保留的巨细。这是一个很好的指示,可以指示继续分析的位置。
- 主宰树(Dominator Tree)
Dominator Tree 列出了最大的对象。我们可以将其称为“Keep-Alive Tree”,因为下一级表现了那些立刻被制止进行垃圾回收的对象。右键单击以深入查看:查看传出和传入引用,或查看 GC 根的路径以查看使对象保持运动的引用链。



  • GC Roots 路径(Path to GC Roots)
    GC Roots 的路径表现了制止对象被垃圾回收的引用链。标有黄点的对象是垃圾回收 (GC) Roots,即假定为活着的对象。通常,GC Roots 是当前位于线程或系统类的调用堆栈上的对象。



  • 按类加载器分组的支配树( Dominator Tree Grouped by Class Loader)
    任何符合的架构都会通过差别的类加载器加载组件。内存分析器的许多视图允许您按类加载器对对象进行分组,从而轻松地按组件分析内存。要将类加载器映射到故意义的组件名称(例如插件 ID),可以使用插件名称解析器。

三、LeakCanary

官网:https://square.github.io/leakcanary/

Gradle 配置
  1. dependencies {
  2.   // debugImplementation because LeakCanary should only run in debug builds.
  3.   debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
  4. }
复制代码
不需要更改代码!需要是debug Build Variants 编译的APK即可。
LeakCanary 自动检测以下对象的泄漏:


  • destroyed Activity instances
  • destroyed Fragment instances
  • destroyed fragment View instances
  • cleared ViewModel instances
  • destroyed Service instance
当应用程序存在内存泄漏时,一样平常的源自我们频仍的一些利用,导致一些资源没有实时回收。当LeakCanary检测到内存泄漏时,会自动Capture heap dump,并且有关照提示,我们可以直接在手机上查看分析结果。
也可以通过 Logcat 过滤关键字 LeakCanary
  1. server@dev-fj-srv:$ adb logcat -s LeakCanary
  2. --------- beginning of main
  3. 07-19 17:53:33.279 17243 17261 D LeakCanary: LeakCanary is running and ready to detect memory leaks.
  4. 07-19 17:53:35.348 17243 17243 D LeakCanary: Watching instance of androidx.coordinatorlayout.widget.CoordinatorLayout (com.android.deskclock.stopwatch.StopwatchFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key ac040ec7-3aa5-479f-9713-f0d13d337cf1
  5. 07-19 17:53:36.306 17243 17243 D LeakCanary: Watching instance of android.widget.LinearLayout (com.android.deskclock.AlarmClockFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key 9108d714-d6bf-4f44-baf7-e24a256ecf98
  6. 07-19 17:53:36.326 17243 17243 D LeakCanary: Watching instance of androidx.coordinatorlayout.widget.CoordinatorLayout (com.android.deskclock.ClockFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key 706ac657-99b7-4938-94e9-84a5877dc58b
  7. 07-19 17:53:36.662 17243 17243 D LeakCanary: Watching instance of androidx.coordinatorlayout.widget.CoordinatorLayout (com.android.deskclock.stopwatch.StopwatchFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key f295fa5b-486f-407c-a352-0091c8bab6fc
  8. 07-19 17:53:36.802 17243 17243 D LeakCanary: Watching instance of com.android.deskclock.timer.TimerItem (com.android.deskclock.timer.TimerItemFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key ff32d891-06a9-4ec1-9e3d-6225aabb4edb
  9. 07-19 17:53:36.803 17243 17243 D LeakCanary: Watching instance of android.widget.FrameLayout (com.android.deskclock.timer.TimerFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key 464d6297-f4b7-4eaa-820c-f054e71c3827
复制代码
发生泄漏时的日志
  1. 07-15 11:04:29.838 31292 31384 D LeakCanary: 2 APPLICATION LEAKS
  2. 07-15 11:04:29.838 31292 31384 D LeakCanary:
  3. 07-15 11:04:29.838 31292 31384 D LeakCanary: References underlined with "~~~" are likely causes.
  4. 07-15 11:04:29.838 31292 31384 D LeakCanary: Learn more at https://squ.re/leaks.
  5. 07-15 11:04:29.838 31292 31384 D LeakCanary:
  6. 07-15 11:04:29.838 31292 31384 D LeakCanary: 295210 bytes retained by leaking objects
  7. 07-15 11:04:29.838 31292 31384 D LeakCanary: Signature: 23d55def87c64b7c2617db75bc4bd2fb6fa36103
  8. 07-15 11:04:29.838 31292 31384 D LeakCanary: ┬───
  9. 07-15 11:04:29.838 31292 31384 D LeakCanary: │ GC Root: Thread object
  10. 07-15 11:04:29.838 31292 31384 D LeakCanary: │
  11. 07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ com.android.internal.os.BackgroundThread instance
  12. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: NO (MessageQueue↓ is not leaking)
  13. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    Retaining 293.6 kB in 4729 objects
  14. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    Thread name: 'android.bg'
  15. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    ↓ HandlerThread.mLooper
  16. 07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ android.os.Looper instance
  17. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: NO (MessageQueue↓ is not leaking)
  18. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    ↓ Looper.mQueue
  19. 07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ android.os.MessageQueue instance
  20. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: NO (MessageQueue#mQuitting is false)
  21. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    HandlerThread: "android.bg"
  22. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    ↓ MessageQueue[0]
  23. 07-15 11:04:29.838 31292 31384 D LeakCanary: │                  ~~~
  24. 07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ android.os.Message instance
  25. 07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: UNKNOWN
  26. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Retaining 295.3 kB in 4733 objects
  27. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.what = 0
  28. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.when = 422519759 (6418 ms before heap dump)
  29. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.obj = null
  30. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.callback = instance @322313336 of android.media.MediaScannerConnection$$ExternalSyntheticLambda0
  31. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.target = instance @319818712 of android.os.Handler
  32. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    ↓ Message.callback
  33. 07-15 11:04:29.839 31292 31384 D LeakCanary: │              ~~~~~~~~
  34. 07-15 11:04:29.839 31292 31384 D LeakCanary: ├─ android.media.MediaScannerConnection$$ExternalSyntheticLambda0 instance
  35. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Leaking: UNKNOWN
  36. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Retaining 295.2 kB in 4732 objects
  37. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    f$0 instance of com.xxx.filemanager.activity.FileExplorerActivity with mDestroyed = true
  38. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    ↓ MediaScannerConnection$$ExternalSyntheticLambda0.f$0
  39. 07-15 11:04:29.839 31292 31384 D LeakCanary: │                                                       ~~~
  40. 07-15 11:04:29.839 31292 31384 D LeakCanary: ╰→ com.xxx.filemanager.activity.FileExplorerActivity instance
  41. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     Leaking: YES (ObjectWatcher was watching this because com.xxx.filemanager.activity.FileExplorerActivity
  42. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     received Activity#onDestroy() callback and Activity#mDestroyed is true)
  43. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     Retaining 295.2 kB in 4730 objects
  44. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     key = 9bf6a560-07b5-4e49-9836-d585492293bc
  45. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     watchDurationMillis = 5248
  46. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     retainedDurationMillis = 247
  47. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     mApplication instance of com.xxx.filemanager.FMApplication
  48. 07-15 11:04:29.839 31292 31384 D LeakCanary: ​     mBase instance of android.app.ContextImpl
  49. 07-15 11:04:29.839 31292 31384 D LeakCanary:
  50. 07-15 11:04:29.839 31292 31384 D LeakCanary: 280683 bytes retained by leaking objects
  51. 07-15 11:04:29.839 31292 31384 D LeakCanary: Signature: 6f2f983de93277fc32f3e5da91ae6581b246e338
  52. 07-15 11:04:29.839 31292 31384 D LeakCanary: ┬───
  53. 07-15 11:04:29.839 31292 31384 D LeakCanary: │ GC Root: Thread object
  54. 07-15 11:04:29.839 31292 31384 D LeakCanary: │
  55. 07-15 11:04:29.839 31292 31384 D LeakCanary: ├─ com.android.internal.os.BackgroundThread instance
  56. 07-15 11:04:29.839 31292 31384 D LeakCanary: │    Leaking: UNKNOWN
  57. 07-15 11:04:29.840 31292 31384 D LeakCanary: │    Retaining 293.6 kB in 4729 objects
  58. 07-15 11:04:29.840 31292 31384 D LeakCanary: │    Thread name: 'android.bg'
  59. 07-15 11:04:29.840 31292 31384 D LeakCanary: │    ↓ BackgroundThread<Java Local>
  60. 07-15 11:04:29.840 31292 31384 D LeakCanary: │                      ~~~~~~~~~~~~
  61. 07-15 11:04:29.840 31292 31384 D LeakCanary: ╰→ com.xxx.filemanager.activity.FileExplorerActivity instance
  62. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     Leaking: YES (ObjectWatcher was watching this because com.freeme.filemanager.activity.FileExplorerActivity
  63. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     received Activity#onDestroy() callback and Activity#mDestroyed is true)
  64. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     Retaining 280.7 kB in 4592 objects
  65. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     key = c6233ed5-0480-4c73-8eb1-addd3d520b66
  66. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     watchDurationMillis = 6531
  67. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     retainedDurationMillis = 1531
  68. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     mApplication instance of com.freeme.filemanager.FMApplication
  69. 07-15 11:04:29.840 31292 31384 D LeakCanary: ​     mBase instance of android.app.ContextImpl
  70. 07-15 11:04:29.840 31292 31384 D LeakCanary: ====================================
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81429

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表