workandroid杂记Android

Android 性能优化&内存篇

2016-12-22  本文已影响10250人  zyq_neuq

本篇主要讲解android内存性能优化之检测方案。内存性能主要包括内存泄漏, 内存抖动, 内存持续增长(但GC后会下降), 内存占用过大等问题。

Android内存分析方向:

本篇主要讲解Java内存分析。

一. 日志分析

查看日志中是否有频繁的GC。通常通过log,我们可以初步定为大部分内存等问题。

二. 常见内存泄漏查找

Context 泄漏, 主要为Activity 传递泄漏, context 未使用applciationConext 在单例创建时。
Handler 泄漏 , handler中持有view ,context 等做耗时操作。
Cursor 泄漏 , cursor未关闭
register 未 unregister
Bitmap
adapter 未使用convertView
不良代码等

三. 命令dumpsys meminfo分析

adb shell dumpsys meminfo com.i2finance.shexpress 
Applications Memory Usage (kB):
Uptime: 142597122 Realtime: 236611715

** MEMINFO in pid 25126 [com.i2finance.shexpress] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    61111    61084        0        0    69888    64350     5537
  Dalvik Heap    49451    49316        0        0    71737    67348     4389
 Dalvik Other     3333     3332        0        0                           
        Stack      960      960        0        0                           
       Cursor       12       12        0        0                           
       Ashmem      130       88        0        0                           
      Gfx dev    23780    23780        0        0                           
    Other dev        4        0        4        0                           
     .so mmap     4373      396     3108        0                           
    .jar mmap       80        0       76        0                           
    .apk mmap    17986       64    17580        0                           
    .ttf mmap       96        0       80        0                           
    .dex mmap    15729       16    14244        0                           
    .oat mmap     2378        0      624        0                           
    .art mmap     1859     1624        8        0                           
   Other mmap     2039       12     1308        0                           
      Unknown    84240    84240        0        0                           
        TOTAL   267561   224924    37032        0   141625   131698     9926

 App Summary
                       Pss(KB)
                        ------
           Java Heap:    50948
         Native Heap:    61084
                Code:    36188
               Stack:      960
            Graphics:    23780
       Private Other:    88996
              System:     5605

               TOTAL:   267561      TOTAL SWAP (KB):        0

 Objects
               Views:      429         ViewRootImpl:        2
         AppContexts:        2           Activities:        1
              Assets:        7        AssetManagers:        3
       Local Binders:       37        Proxy Binders:       31
       Parcel memory:       26         Parcel count:       65
    Death Recipients:        2      OpenSSL Sockets:        6

 SQL
         MEMORY_USED:      567
  PAGECACHE_OVERFLOW:      157          MALLOC_SIZE:       62

 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4       24             45         5/24/6  /data/user/0/com.i2finance.shexpress/databases/pa_data_cache.db
         4       28             19         1/16/2  /data/user/0/com.i2finance.shexpress/databases/mpush.db
         4       60             37         5/18/6  /data/user/0/com.i2finance.shexpress/databases/fstandard.db
         4       60             91      466/22/11  /data/user/0/com.i2finance.shexpress/databases/fstandard.db (2)
         4       24             40         5/24/6  /data/user/0/com.i2finance.shexpress/databases/pa_data_cache.db

 Asset Allocations
    zip:/data/user/0/com.i2finance.shexpress/files/paanydoor_resource_3.5.0.36.jar:/resources.arsc: 67K

meminfo的信息中各字段都是什么含义, 要理解各字段含义,我们才好进行内存的优化。

首先了解两个概念:

通常我们需要关注PSS TOTALPrivate Dirty .

四. Heap Viewer

Heap Viewer 能做什么?

AS中点击机器人图标打开Android Device Mointor, 如下:
选中进程进行Heap 分析,点击update heap, 查看右侧的heap标签页

Paste_Image.png

Heap视图显示了堆内存使用的情况,每次垃圾回收都会更新,要查看更新情况, 点击Cause GC即可。
下面的内容显示的是分配的内存,按照类型分类:


Paste_Image.png

如何检查内存泄漏

我们需要在执行查看内存是否有泄漏的用例之前和之后执行GC,即手动点击Cause GC,观察allocated大小,查看内存是否在一个稳定的数值,多次操作,只要内存稳定,即没有内存泄漏, 如果不断变大,即表示有内存泄漏。
该工具也可以用来查看是否会发生内存抖动

五. 生成Dump

分析内存泄漏,我们需要生成相关的内存Dump,那么我们如何生成dump文件来进行分析。

目前有两种方式:

hprof-conv com.i2finance.shexpress.hprof xxx.hprof 转换生成可用的hprof文件。

六. Heap Snapshot

获取Java 堆内存详细信息,可以分析出内存泄漏的问题。
打开Android Studio 的Android Monitor , 选中Memory 标签, 点击Dump heap,生成hprof文件。AS会自动打开该文件,见下图,但是该功能有点弱,建议还是转换成mat可识别的hprof,使用mat进行分析。


Paste_Image.png

七. 使用LeakCanary

使用内存检测软件leakCanary

build.gradle 中增加依赖

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'

Applciationoncreate中增加语句

LeakCanary.install(this);

查看leak详情。
当发生内存泄漏时,会生成leak 报告, 报告中会详细写明具体发现内存泄漏的语句。
其原理,可以自行上网搜索查看一下。

八. Allocation Tracker(DeviceMonitor)

Allocation Tracker 能够追踪内存分配信息, 按照顺序排列,这样我们能够清晰的看出来每一个内存对象是怎么一步一步的分配出来的。比如内存抖动的可疑点,我们可以通过查看其内存分配轨迹来查看段时间内有多少相同或相似对象被创建,进而找到问题发生的代码。

操作步骤:

  1. 进入追踪界面
  2. 点击start Tracking 按钮,开始跟踪内存分配轨迹
  3. 操作用例
  4. 点击Get Allocations,获取内存分配轨迹。


    Paste_Image.png

如上图,上行app 从后台切换道前台时会调用onResume,可以追踪到最后创建了多个Configuration对象。

上图中,Allcated class 表示创建的类型,第一个Allocated in 表示在哪个类中, 第二个Allocated in 表示在哪个方法中。

查看源代码如下:

public Resources getResources() {
    Resources res = super.getResources();
    Configuration config = new Configuration();
    config.setToDefaults();
    try {
        res.updateConfiguration(config, res.getDisplayMetrics());
    }catch (Exception e){
        e.printStackTrace();
    }
    return res;
}

九. Allocation Tracker(AndroidMonitor)

功能同Allocation Tracker(Andorid Device) , 但是展示更酷炫,更全面。
打开Android Monitor, 选中Memory 标签 , 点击图标

Paste_Image.png
, 进行内存tracker, 再次点击结束tracker。As会自动打开tracker文件。
下面我们详细看一下这个面板:
Paste_Image.png

AS给我们提供了多种展示方式

AS 还为我们提供了统计,点击饼状图标按钮即可。

分为两种展示形式,有柱状图和轮胎图,分配比例可选分配次数和占用内存大小:

下图为Sunburst + by Allocator
一个内存的完整路径


Paste_Image.png

比如上行的首页中trace 的数据, 我们看下我们自己的包:

Paste_Image.png

会发现,最外围有很多PageScrollEvent 对象, 我们去看下源代码:

代码如下, 我们发现自动loop的viewpager 每次滑动都会创建多个PageScorllEvent 对象。这样也就对应上面这幅图了。

private class PageChangeListener implements OnPageChangeListener {
    private PageChangeListener() {
    }

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (isLoop) {
            int count = getAdapter().getCount();
            if (position < 1 || position > count - 2) {
                return;
            }
        }
        LoopViewPager.this.mEventDispatcher.dispatchEvent(new PageScrollEvent(LoopViewPager.this.getId(), SystemClock.uptimeMillis(), position, positionOffset));
    }
Paste_Image.png

十. MAT

MAT工具全称为Memory Analyzer Tool,一款详细分析Java堆内存的工具,该工具非常强大,为了使用该工具,我们需要hprof文件.

HPROF文件存储的是特定时间点,java进程的内存快照。有不同的格式来存储这些数据,总的来说包含了快照被触发时java对象和类在heap中的情况。由于快照只是一瞬间的事情,所以heap dump中无法包含一个对象在何时、何地(哪个方法中)被分配这样的信息。

几个关键概念

1. 预览信息

打开dump 文件,通常我们需要关注一下几个重要信息, 内存占用饼图,Actions部分的Histogram, Top Consumers.
我们打开Top Consumers,会生成一个报告,我们可以Biggets Objects overview, 能够看到主要内存占用者

Paste_Image.png

点击下面的biggest Objects 可以查看具体的地址。
还有Biggest Top Level Dominator Classes , 可以看到主要占用内存的都是些什么东东。

2. dump分析

2.1 Histogram

MAT中Histogram的主要作用是查看一个instance的数量,一般用来查看自己创建的类的实例的个数。 可以分不同维度来查看对象的Dominator Tree视图,Group by class、Group by class loader、Group by package 和Histogram类似,时间久了,通过多次对比也可以把溢出对象找出来。 Histogram 中可以分Group,Thread 区分信息。 通常为:选中某一项-> show objects and class -> by incoming reference->merge shortest path to gc root -> exclude weadk reference
等流程来查看具体情况。

可以在上面过滤相关包名,查看到具体类型, 关注objects个数, 表示内存dump 中有多少个相关类型对象, 比如不改存在的 对象存在了,或者有的对象内存中有太多的份数, 这样就可以进行一个全面分析。

也可以选择Group by package ,这样方便根据package来进行分析。


Paste_Image.png

也可以选择thread来进行分析, 这样查看占用内存最多的线程,这些线程可能为有内存问题的线程。

点击右键常用的几个选项:

2.2 Debug Bitmap

图片一直是内存占用的一个大头,也是引起内存泄露,OOM的常客。所以对图片的分析是需要非常了解,这样才能更好的优化项目。*注意:图片在内存中占用的大小:ARGB_8888 类型的图片 为 内存中图片宽度*内存中图片高度4, 此处需要注意原始图片宽高和内存图片宽高不一致,包括拉伸和压缩,尤其是图片位置放错,比如1080p设备,xxxhdpi下面没有图片,会去别的目录下寻找图片,此时将会对图片拉伸。 **
下面我们来看一下图片的处理。通常dump信息中图片表现为两种类型,Bitmap, byte[]。我们需要知道该图片是哪张图片,这样才能好优化相关的图片代码。

选中mBuffer-> 右键选中Copy-> 选择Save Value To File -> 生成一个xxx.data 文件。

下一步是选中对应的bitmap,打开Inspector 窗口,查看bitmap的尺寸,并且使用GIMP工具(可以安装一个,开源的)打开刚才的data文件,图像类型选择RGB Alpha, 宽度和高度填入图像的宽高,打开即可。

Paste_Image.png Paste_Image.png
2.3 堆对比

通常为了分析内存是否泄露,内存是否持续增长但没有释放等问题,我们需要dump两次来进行内存堆的对比。

打开两个或多个dump文件,打开Navigation History视图,点击Historgam,选择Add to Comp are Basket,最后选中Compare the Result 。

Paste_Image.png

在对比结果中,主要分析类型或者对象的数量是否有变化, 内存是否有变化。

通过以上手段,我们可以定位到大部分内存问题。

上一篇下一篇

猜你喜欢

热点阅读