tsx81429 发表于 2024-9-3 11:16:47

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

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

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

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

[*]在时间轴上查找大概会导致性能题目的不理想的内存分配模式。
[*]转储 Java 堆以查看在任何给定时间有哪些对象在占用内存。在一个较长的时间段内进行多次堆转储有助于识别内存泄漏。
[*]纪录正常条件和极度条件下用户交互期间的内存分配情况,从而准确识别您的代码是否在短时间内分配了过多对象,或所分配的对象是否出现了泄漏。
https://i-blog.csdnimg.cn/direct/cba8888b40814a3aa1520b6152a3e028.png
捕获堆转储
堆转储表现在您捕获堆转储时您的应用中哪些对象正在使用内存。特别是在长时间的用户会话后,堆转储会表现您认为不应再位于内存中却仍在内存中的对象,从而帮助识别内存泄漏。
捕获堆转储后,您可以查看以下信息:


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


[*] 欣赏列表以查找堆计数异常大且大概存在泄漏的对象。为帮助查找已知类,点击 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。如许会从堆转储中选择相应的实例,从而向您表现它自己的实例数据。
https://i-blog.csdnimg.cn/direct/a21b5ea696464d8ab338c91c862625ed.png
在您的堆转储中,请注意由下列任意情况引起的内存泄漏:


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

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


[*] 概述(Overview)
获取堆转储的概览:顶部是对象的巨细和总数,然后是包罗最大对象和链接的饼图,以便继续分析。
https://i-blog.csdnimg.cn/direct/6a9f37e195ea4ed19c3cd2047f39b100.png
[*] 直方图(Histogram)
直方图列出了按种别分组的对象。内存分析器可以非常快速地估算出保留的巨细。这是一个很好的指示,可以指示继续分析的位置。
https://i-blog.csdnimg.cn/direct/0e7db624c5ee4ce78b1acb913e982e40.png- 主宰树(Dominator Tree)
Dominator Tree 列出了最大的对象。我们可以将其称为“Keep-Alive Tree”,因为下一级表现了那些立刻被制止进行垃圾回收的对象。右键单击以深入查看:查看传出和传入引用,或查看 GC 根的路径以查看使对象保持运动的引用链。
https://i-blog.csdnimg.cn/direct/6fdaf915ede9427784427da9f7ab105c.png


[*]GC Roots 路径(Path to GC Roots)
GC Roots 的路径表现了制止对象被垃圾回收的引用链。标有黄点的对象是垃圾回收 (GC) Roots,即假定为活着的对象。通常,GC Roots 是当前位于线程或系统类的调用堆栈上的对象。
https://i-blog.csdnimg.cn/direct/6ec171bbaf9e41fda9aa7e9c51ed90ac.png


[*]按类加载器分组的支配树( Dominator Tree Grouped by Class Loader)
任何符合的架构都会通过差别的类加载器加载组件。内存分析器的许多视图允许您按类加载器对对象进行分组,从而轻松地按组件分析内存。要将类加载器映射到故意义的组件名称(例如插件 ID),可以使用插件名称解析器。
https://i-blog.csdnimg.cn/direct/505d4d6a3afd4416a33dbf6fdcd1c52d.png
三、LeakCanary

官网:https://square.github.io/leakcanary/
https://i-blog.csdnimg.cn/direct/94f1f51c0c144cf28d44c10a91a024df.png
Gradle 配置
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
}
不需要更改代码!需要是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
server@dev-fj-srv:$ adb logcat -s LeakCanary
--------- beginning of main
07-19 17:53:33.279 17243 17261 D LeakCanary: LeakCanary is running and ready to detect memory leaks.
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
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
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
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
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
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
发生泄漏时的日志
07-15 11:04:29.838 31292 31384 D LeakCanary: 2 APPLICATION LEAKS
07-15 11:04:29.838 31292 31384 D LeakCanary:
07-15 11:04:29.838 31292 31384 D LeakCanary: References underlined with "~~~" are likely causes.
07-15 11:04:29.838 31292 31384 D LeakCanary: Learn more at https://squ.re/leaks.
07-15 11:04:29.838 31292 31384 D LeakCanary:
07-15 11:04:29.838 31292 31384 D LeakCanary: 295210 bytes retained by leaking objects
07-15 11:04:29.838 31292 31384 D LeakCanary: Signature: 23d55def87c64b7c2617db75bc4bd2fb6fa36103
07-15 11:04:29.838 31292 31384 D LeakCanary: ┬───
07-15 11:04:29.838 31292 31384 D LeakCanary: │ GC Root: Thread object
07-15 11:04:29.838 31292 31384 D LeakCanary: │
07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ com.android.internal.os.BackgroundThread instance
07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: NO (MessageQueue↓ is not leaking)
07-15 11:04:29.838 31292 31384 D LeakCanary: │    Retaining 293.6 kB in 4729 objects
07-15 11:04:29.838 31292 31384 D LeakCanary: │    Thread name: 'android.bg'
07-15 11:04:29.838 31292 31384 D LeakCanary: │    ↓ HandlerThread.mLooper
07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ android.os.Looper instance
07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: NO (MessageQueue↓ is not leaking)
07-15 11:04:29.838 31292 31384 D LeakCanary: │    ↓ Looper.mQueue
07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ android.os.MessageQueue instance
07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: NO (MessageQueue#mQuitting is false)
07-15 11:04:29.838 31292 31384 D LeakCanary: │    HandlerThread: "android.bg"
07-15 11:04:29.838 31292 31384 D LeakCanary: │    ↓ MessageQueue
07-15 11:04:29.838 31292 31384 D LeakCanary: │                  ~~~
07-15 11:04:29.838 31292 31384 D LeakCanary: ├─ android.os.Message instance
07-15 11:04:29.838 31292 31384 D LeakCanary: │    Leaking: UNKNOWN
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Retaining 295.3 kB in 4733 objects
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.what = 0
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.when = 422519759 (6418 ms before heap dump)
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.obj = null
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.callback = instance @322313336 of android.media.MediaScannerConnection$$ExternalSyntheticLambda0
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Message.target = instance @319818712 of android.os.Handler
07-15 11:04:29.839 31292 31384 D LeakCanary: │    ↓ Message.callback
07-15 11:04:29.839 31292 31384 D LeakCanary: │            ~~~~~~~~
07-15 11:04:29.839 31292 31384 D LeakCanary: ├─ android.media.MediaScannerConnection$$ExternalSyntheticLambda0 instance
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Leaking: UNKNOWN
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Retaining 295.2 kB in 4732 objects
07-15 11:04:29.839 31292 31384 D LeakCanary: │    f$0 instance of com.xxx.filemanager.activity.FileExplorerActivity with mDestroyed = true
07-15 11:04:29.839 31292 31384 D LeakCanary: │    ↓ MediaScannerConnection$$ExternalSyntheticLambda0.f$0
07-15 11:04:29.839 31292 31384 D LeakCanary: │                                                       ~~~
07-15 11:04:29.839 31292 31384 D LeakCanary: ╰→ com.xxx.filemanager.activity.FileExplorerActivity instance
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   Leaking: YES (ObjectWatcher was watching this because com.xxx.filemanager.activity.FileExplorerActivity
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   received Activity#onDestroy() callback and Activity#mDestroyed is true)
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   Retaining 295.2 kB in 4730 objects
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   key = 9bf6a560-07b5-4e49-9836-d585492293bc
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   watchDurationMillis = 5248
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   retainedDurationMillis = 247
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   mApplication instance of com.xxx.filemanager.FMApplication
07-15 11:04:29.839 31292 31384 D LeakCanary: ​   mBase instance of android.app.ContextImpl
07-15 11:04:29.839 31292 31384 D LeakCanary:
07-15 11:04:29.839 31292 31384 D LeakCanary: 280683 bytes retained by leaking objects
07-15 11:04:29.839 31292 31384 D LeakCanary: Signature: 6f2f983de93277fc32f3e5da91ae6581b246e338
07-15 11:04:29.839 31292 31384 D LeakCanary: ┬───
07-15 11:04:29.839 31292 31384 D LeakCanary: │ GC Root: Thread object
07-15 11:04:29.839 31292 31384 D LeakCanary: │
07-15 11:04:29.839 31292 31384 D LeakCanary: ├─ com.android.internal.os.BackgroundThread instance
07-15 11:04:29.839 31292 31384 D LeakCanary: │    Leaking: UNKNOWN
07-15 11:04:29.840 31292 31384 D LeakCanary: │    Retaining 293.6 kB in 4729 objects
07-15 11:04:29.840 31292 31384 D LeakCanary: │    Thread name: 'android.bg'
07-15 11:04:29.840 31292 31384 D LeakCanary: │    ↓ BackgroundThread<Java Local>
07-15 11:04:29.840 31292 31384 D LeakCanary: │                      ~~~~~~~~~~~~
07-15 11:04:29.840 31292 31384 D LeakCanary: ╰→ com.xxx.filemanager.activity.FileExplorerActivity instance
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   Leaking: YES (ObjectWatcher was watching this because com.freeme.filemanager.activity.FileExplorerActivity
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   received Activity#onDestroy() callback and Activity#mDestroyed is true)
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   Retaining 280.7 kB in 4592 objects
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   key = c6233ed5-0480-4c73-8eb1-addd3d520b66
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   watchDurationMillis = 6531
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   retainedDurationMillis = 1531
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   mApplication instance of com.freeme.filemanager.FMApplication
07-15 11:04:29.840 31292 31384 D LeakCanary: ​   mBase instance of android.app.ContextImpl
07-15 11:04:29.840 31292 31384 D LeakCanary: ====================================

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android性能优化之内存泄漏优化(工具篇)