性能优化内存泄漏

Android内存泄漏分析

2019-07-01  本文已影响18人  码上述Andy

1 .工具介绍

1.1使用Android Studio查看内存快照:

(1)可以查看对象对应的文件目录及内容,比如Bitmap可以看到对应的图片。
(2)可在InstanceView区右击可快速jump to source。
(3)不方便查看对象的最短引用链。


image.png

如图分为Heap Dump,Instance View, References三个区域。
其中Heap Dump区域:
Arrange by class:按照类进行分组
Arrange by package:按照包进行分组
Arrange by callstack:按照调用堆栈进行分组
说明:
Total Count 该类的实例总数
Sizeof 单个实例所占空间大小
Shallow Size 堆里所有实例大小总和(Heap Count * Sizeof)
Retained Size 该类所有实例所支配的内存大小
Heap Count 所选择的堆中该类的实例的数量
Depth GC根节点到所选实例的最短路径的深度

2.1使用MAT查看内存快照(推荐用):

(1)可以方便查看对象的对象的最短引用链。
(2)不方便定位对象对应的资源。

点击Histogram可显示内存快照文件


image.png

建议两个工具配合一起使用。

2.内存信息

通过adb查看当前进程的内存信息:

adb shell dumpsys meminfo <包名>

以我们产品为例:

adb shell dumpsys meminfo com.baidu.launcher      
* daemon not running; starting now at tcp:5037
* daemon started successfully
Applications Memory Usage (in Kilobytes):
Uptime: 81529000 Realtime: 81529000

** MEMINFO in pid 11898 [com.baidu.launcher] **
                   Pss  Private  Private  SwapPss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap     9685     9660        0      127    11520     9884     1635
  Dalvik Heap     5341     5296       16       39     6069     3051     3018
 Dalvik Other     3284     3284        0       36                           
        Stack       64       64        0        0                           
       Ashmem       23        0        0        0                           
    Other dev        8        0        8        0                           
     .so mmap    11878      420     8212       20                           
    .apk mmap     1039        0       80        0                           
    .dex mmap    22381        4    11124        4                           
    .oat mmap      835        0      156        0                           
    .art mmap     3184     2628       56      345                           
   Other mmap       39        0        0        4                           
   EGL mtrack     5226     5226        0        0                           
    GL mtrack     8963     8963        0        0                           
      Unknown     2490     2488        0      132                           
        TOTAL    75147    38033    19652      707    17589    12935     4653
 
 App Summary
                       Pss(KB)
                        ------
           Java Heap:     7980
         Native Heap:     9660
                Code:    19996
               Stack:       64
            Graphics:    14189
       Private Other:     5796
              System:    17462
 
               TOTAL:    75147       TOTAL SWAP PSS:      707
 
 Objects
               Views:       33         ViewRootImpl:        1
         AppContexts:        4           Activities:        0
              Assets:        4        AssetManagers:        2
       Local Binders:       41        Proxy Binders:       29
       Parcel memory:       22         Parcel count:       87
    Death Recipients:        1      OpenSSL Sockets:        4
            WebViews:        0
 
 SQL
         MEMORY_USED:      287
  PAGECACHE_OVERFLOW:      179          MALLOC_SIZE:      117
 
 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4      200                   2839/252/25  /data/user/0/com.baidu.launcher/databases/puffer.db
         4       20                        0/15/1  /data/user/0/com.baidu.launcher/databases/crabstat.db
         4       56                        3/35/5  /data/user/0/com.baidu.launcher/databases/duershow.db
         4       12                         0/0/0    (attached) temp

3.内存快照获取三种方式

3.1 通过android studio ----Memory Profiler
3.2 打开monitor(/Users/zhouwen/Library/Android/sdk/tools/monitor)


image.png

选取自己的应用进程名-->点击dump HPROF file按钮即可生成hprof文件。
3.3 通过adb shell am dumpheap也可以dump出hprof文件。

adb shell 
cd data/local/tmp
am dumpheap <包名> output.hprof

➜  ~ adb shell
tb8765ap1_bsp_1g:/ # cd data/local/tmp
tb8765ap1_bsp_1g:/data/local/tmp # am dumpheap com.baidu.launcher output.hprof

4.内存泄露分析

4.1MAT(Memory Analyzer Tool)以我们项目为例:

4.1.1 内存快照获取

普遍的方式通过monitor dump对应的内存快照或者通过adb shell进入data/local/tmp目录下 am dumpheap 出对应应用的hprof文件。以我们项目为例,我们则通过跑稳定性当内存飙升到大于60M以上,会自动adb去dump一份内存快照上传到后台。

4.1.2 内存快照转换

/Users/zhouwen/Library/Android/sdk/hprof-conv input.hprof  output.hprof

通过以上转换成mat可以解析的文件。

4.1.3 占比

image.png

以上三个可能出现泄露问题的占比分布。

4.1.4 内存可能泄露的点

image.png
image.png

如上图所示byte[]数组,Bitmap,NewBgView可能出现泄露。

4.1.5 通过搜索关键字可以搜索到相关泄露对象,比如Bitmap,Activity等。

image.png
image.png

我自己一般分析内存步骤如下:

第一步:点击historgram按钮


image.png

通过Objects(对象数量)可以看出AlarmHomeCard和BaseCard对象数量比较多,可能存在泄露。
第二步:右击Objects数量比较多的class Name-->Merge Shortest Paths To Gc Roots-->exclude weak/soft references然后一个一个点开引用链分析。
以下是HomeCardGroup的第六个匿名内部类实例被handler引用着导致释放不了,对应NewBgView这个泄露点。


image.png
以下是TimerHomeCard的第一个匿名内部类实例被handler引用导致界面退出释放不了。
image.png

通过以上分析结合代码逻辑去看看到底是那个地方出现了内存泄露。

泄露原因及优化

原因:
上面两个内存泄露案例都有因为匿名内部类默认引用外部类,当界面关闭,导致当前界面一直释放不了,内存泄露。
解决办法:
将匿名内部类改成static,使其对外部类没有引用。

4.1.6参数说明

Objects:对象数量
Shallow Size:堆里所有实例大小总和(Heap Count * Sizeof)
Retained Size:该类所有实例所支配的内存大小
List objects:
with outgoing references: 列出这个对象被哪些对象引用
with ingoing references:列出这个对象持有哪些对象引用
Merge Shorts Path To GC Roots:
with all references:所有的引用。
exclude weak references: 去除弱引用。
exclude weak/soft references:去除弱/软引用(用的最多)。
其他就不列举了。

4.2 android studio分析(直接把内存快照拖至到as里)

这里就不介绍了,分析起来没Mat工具方便。

5.常见的内存泄露地方

5.1.Handler内存泄露,最好使用弱引用,释放时清除对应消息。
5.2.Cursor,File等使用完未关闭,及时关闭以便缓存数据及时回收。
5.3.过多的WebView,一个应用使用一个WebView并且最好放到单独的进程中。
5.4.注册对象未注销掉,导致界面一直释放不了。
5.5.及时释放容器对象。
5.6.匿名内部类持有外部引用。
5.7.单例中的Context生命周期大于本身Context生命周期

上一篇 下一篇

猜你喜欢

热点阅读