性能优化总结1-整体概括

2019-07-15  本文已影响0人  刘佳阔

1. 概述

最近看了三本性能优化相关的书,<Android 移动性能实战>,<Android 应用性能优化最佳实践>,<高性能Android应用开发>,两本是开发人员写的,一本是测试人员写的.发现对比来看有很多相通的地方.所以把三本书总结到一起.做一个系列的笔记.
整体上,内容都是分为ui,内存,文件,CPU.网络.电量.电量对我现在的阶段,不是很重要的东西,这里就随便记录一些.然后其他几个步骤大体是讲原理.讲常见的知识点,讲修复评测工具.因为涉及内容很广.所以我也把目前不是很了解的知识点额外记录下来了.文章中会做一些讲解.先看整体的思维导图吧.


性能优化知识点总结.png

2.ui优化

原理

1. 绘制原理

1.1 APP端

1.2 系统端

2. 刷新机制

3. 卡顿原因

案例

3.内存优化

原理

1. java对象声明周期

  1. created 分配空间,初始化static变量和成员变量
  2. inuse 至少被一个强引用对象所持有.
  3. invisible 没有被程序的任何强引用所持有.但可能被某些虚拟机下静态变量或者jni等强引用所持有
  4. unreachable 不可达,在不可见的基础上,也没被任何虚拟机或jni等强引用所持有.总是就是没有任何对象锁持有.
  5. collected 对象是不可达状态,且垃圾收集器准备回收这段内存了.就变成收集阶段.如果对象重写了finanlize方法,会执行finanlize
  6. finanlized 对象的finanlize方法执行完成后仍处于不可达状态.就进入终结阶段等待回收
  7. deallocated 垃圾回收对这段内存进行回收.对象彻底消失.

综上所述,当对象处于3却没法到达4的状态时,就是对象发生了内存泄露.
解释连接 https://www.jianshu.com/p/72a0e26d35bc

2. 内存分配

安卓的所有进程都是从zygote进程中fork出来的.安卓通过显示分配共享内存区域来实现不同进程之间共享RAM的机制.例如farmwork的代码.基础的so库.都是所有进程共享的.但是每个进程也有自己独立的内存. 独立的内存就是Uss,共享内存加独立的内存就是Pss.
而安卓虚拟机中又把内存对分成不同的区块.


内存堆

3. 内存回收机制

安卓内存分为三个区域,年轻代,年老代,持久代.三个区域采用不同的gc算法.当对象在年轻代中经历几次gc还没被杀死.就进入了年老代.同样,在停留达到一定时间就进入持久代,持久代中也存放静态的方法和类,持久代对gc没什么效果.


内存模型

年轻代又分成三个区,他采用copying算法,他进行gc更加频繁,但gc的速度也相对更快

copying 算法
  将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,
之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收

年老代对象存活时间比较长.采用标记Mark算法.

标记回收算法(Mark and Sweep GC)
  从"GC Roots"集合开始,将内存整个遍历一次,保留所有可以被GC Roots直接或间接引用到的对象,而剩下的对象都当作垃圾对待并回收,
这个算法需要中断进程内其它组件的执行并且可能产生内存碎片

标记-压缩算法 (Mark-Compact)   
  先需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。
这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。

关于算法和内存堆的详细讲解 https://www.jianshu.com/p/106795175971
垃圾回收讲解 https://blog.csdn.net/omnispace/article/details/50991489

4. Gc类型

GC_FOR_MALLOC: 表示是在堆上分配对象时内存不足触发的GC。 这种gc会在并发时停止所有线程.会卡顿.
GC_CONCURRENT: 当我们应用程序的堆内存达到一定量,或者可以理解为快要满的时候,系统会自动触发GC操作来释放内存。
GC_EXPLICIT: 表示是应用程序调用System.gc、VMRuntime.gc接口或者收到SIGUSR1信号时触发的GC。
GC_BEFORE_OOM: 表示是在准备抛OOM异常之前进行的最后努力而触发的GC。

5. 内存抖动

内存抖动是由于短时间内有大量对象进出Young Generiation区导致的,它伴随着频繁的GC.就是短时间大量申请对象,然后曲线上涨,但是触发多次gc又导致曲线下降.因此我们要避免短时间创建对象.一般就是循环,或者重复调用的方法中避免创建对象.

内存抖动表现

6. 安卓内存警告 OnTrimMemory

OnTrimMemory是Android在4.0之后加入的一个回调,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方法.OnTrimMemory的主要作用就是指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉.
警告类型

当应用程序是缓存的,也就是不在前台的时候,则会收到以下几种类型的回调:

案例

public class MainActivity extentd Activity{
    onCreate(){
      mHandler.postDelayed(new Runnable() {  //
        @Override
        public void run() {
        // TODO Auto-generated method stub  
        }
    }, 60*60*1000);

    }
}
  1. 避免内存泄露
  1. 减少内存消耗
    //先得到图片的比例,在根据想要的比较计算缩放比.这样可以显示正确的图片大小,而且节省内存资源
   public Bitmap decodeBitmap()
    {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;    // 通过这个bitmap获取图片的宽和高       
        Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/MTXX/3.jpg", options);
        float realWidth = options.outWidth;
        float realHeight = options.outHeight;
        // 计算缩放比       
        int scale = (int) ((realHeight > realWidth ? realHeight : realWidth) / 100);
        if (scale <= 0)
        { scale = 1; }
        options.inSampleSize = scale;
        options.inJustDecodeBounds = false;
        // 注意这次要把options.inJustDecodeBounds 设为 false,这次图片是要读取出来的。      
        bitmap = BitmapFactory.decodeFile("/sdcard/MTXX/3.jpg", options);
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        return bitmap;

3. io优化

原理

1.随机读写问题.

随机读写的性能比顺序读写要效率低,因为随机读写要多次切换读取数据的地址,并且会有写入放大效应,导致效率低下,写入放大效应是值要写入的数据的大小超过了当前块中连续的连续的内存,为了写入新数据,要把快中所有有用的数据全部取出,清空块的数据,再把原来数据和新数据一起写入.

写入在SSD中的数据是不可以直接更新的,只能通过扇区覆盖重写,在覆盖重写之前需要先擦除,而且擦除操作又是不能在扇区上做的,只能在磁盘的块上来完成,擦除块之前需要将原有的还有效的数据先读出,然后在与新来的数据一起写入,这些重复的操作不单会增加写入的数据量 ,还会减少闪存的寿命,更吃光闪存的可用带宽而间接影响随机写入性能。

举个最简单的例子:当要写入一个4KB的数据时,最坏的情况是一个块里已经没有干净空间了,但有无效的数据可以擦除,所以主控就把所有的数据读到缓存,擦除块,缓存里更新整个块的数据,再把新数据写回去,这个操作带来的写入放大就是: 实际写4K的数据,造成了整个块(共512KB)的写入操作,那就是放大了128倍。同时还带来了原本只需要简单一步写入4KB的操作变成:闪存读取(512KB)→缓存改(4KB)→闪存擦除(512KB)→闪存写入(512KB),共四步操作,造成延迟大大增加,速度变慢。

2.文件io问题.

案例

  1. sharedPreferencs每调用一次就是对应一次文件的io操作.所以只在需要时候调用就好.同时改用apply异步提交.
  2. 对于系统中的配置文件.读取后保存在缓存中即可.不需要重复读取
  3. 主线程不要进行io操作.
  4. 用ButeArrayStream和BufferStream代替ObjectStream.因为ObjectStream在保存对象时每个成员都会进行一次IO操作.
  5. inputStream/outputStream流时,用 byte buff[] =new byte[4096] .使用4kb-8kb来提升读写效率
  6. ZipFile用来解压本地文件效率高.他每次传递1-64kb数据给native层解压. ZipInputStream则适应边下载边解压的方式.
  7. 数据库减少重复开闭.在APP退出时进行close即可.
  8. 数据库的autoincrement 字段递增字段会额外增加插入时间.不要滥用
  9. bitmap适应DecodeStream不要适应decodeFile, 在4.4以上效率不高.同时给decodeStream传的文件流是bufferInputStream
  10. sql中频繁插入要使用事务.因为没执行一次插入操作.系统会自动创建事务,如果插入频繁,会频繁创建事务.影响效率.所以要显示的创建事务.
  11. 在序列化对象是使用FlatBuffers.比较高效且节省内存.json序列化效率很低.serializable会销毁额外内存,引起gc. parcelable不适应把对象持有话,他是为了进行ipc传递对象.sharedPreference只能保存简单类型,且会有线程同步锁.
    附一个大神的博客. 性能优化合集.很全面
    https://www.jianshu.com/p/ab057549c582

4. 网络优化

原理

1.业务成功率

业务成功率就是保证网络接口能正常通信,这里通常问题有两个.

  1. 弱信号网络(走进电梯后的情况),解决办法就是APP里进行接口重试,这种弱信号通常都是短时间的.
  2. 拥塞网络
    计算机网络中的带宽、交换节点中的缓存和处理机等,都是网络的资源,在某段时间内,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏,这种情况就叫做拥塞。简单说就是发送发同时发送的数据量超过了这条链路的承受能力.
    详细文章解释https://blog.csdn.net/zhangdaisylove/article/details/47294315
    解决办法是减少不重要的网络请求,减少网络重试
  3. 窗口分为滑动窗口和拥塞窗口。
    滑动窗口是接受数据端使用的窗口大小,用来告知发送端接收端的缓存大小,以此可以控制发送端发送数据的大小,从而达到流量控制的目的,如果传输稳定,滑动窗口会变大来增加发送效率.当网络不好时会降低滑动窗口的大小.滑动窗口其实就是接收方可以承受的数据流入大小.
    拥塞窗口是数据的发送端,拥塞窗口不代表缓存,拥塞窗口指某一源端数据流在一个RTT内可以最多发送的[数据包]数,拥塞窗口是为了提高在链路上发送的效率问题.接收方告诉发送方滑动窗口的大小.但是发送端并不知道网络是否可以正常支持滑动窗口大小的数据,所以就先发一个包.拿到应答后.在发两个包.在三个包.逐渐增大发送包的数量.这样就减少了网络拥塞
    https://juejin.im/post/5c9f1dd651882567b4339bce

2. 网络延时

1.dns解析慢, 通过域名查找IP的过程.dns会按照本地缓存-操作系统缓存-路由器缓存-ISP DNS 缓存服务器-迭代查询来逐步查找域名对应的IP地址,在那里找到就返回给用户.所以这个也是耗时的过程.
2.建立链接,TCP需要三次握手,用来确认通信双方都能接受到数据.这里也是耗时的.可以通过建立长连接来减少三次握手的耗时.


image.png

3.接收窗口.用于拥塞控制.果在发送方和接收方之间存在多个路由器和速率较慢的链路,那么开始时就要发送少量数据.然后慢慢增加数据发送量.随着时间的推移增加发送量. 这个可以调节到合适的接受窗口大小.

3. 带宽成本

减少流量的浪费.也是节省带宽和资源的方式.常用的有对资源进行压缩.对进行增量更新数据,去除重复下载的数据.
图片可以采用 WebP压缩,PNG压缩.文本用gzip来压缩.

案例

  1. webview设置缓存
  2. 网络资源(图片,文本.HTML)进行压缩
  3. 调大服务器的滑动窗口来提高上传速度
  4. 设置请求响应头来来进行缓存
  5. 减少网络连接,使用jobScheduler进行同步`
  6. 监测网络状态

5. 电量优化

原理

  1. 正常情况下,社保在活动状态下使用1秒的耗电量相当于待机2分钟的耗电量.
  2. doze模式
    激活状态, 屏幕点亮
    休眠状态,屏幕休眠,社社保处于唤醒状态(30分钟)
    待闲置状态,准备进入闲置状态(30分钟)
    闲置状态, 社保休眠
    闲置维护状态,队列中的alarm和更新任务短暂的执行的机会
    设备用30分钟从休眠到待闲置,在用30分钟进入闲置状态.进入闲置状态后.设备推迟所以alarm到下一个闲置维护期,这个延时不断增加(1.2.3.6)小时.
    详解连接 https://chendongqi.me/2017/02/28/pm_doze_MeetDoze/
    doze模式有哪些限制
        –网络连接会被禁止
        –Wake Lock会被屏蔽
        –AlarmManager定时任务延迟到下一个maintenance window进行处理,除非使用AlarmManager提供的方法:setAndAllowWhileIdle()或者setExactAndAllowWhileIdle()
        –系统将不扫描热点WIFI
        –同步工作将被禁止
        –不允许JobScheduler进行任务调度

案例

1. alarmManager

AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        Intent in = new Intent();
        in.setClass(context, RebootService.class);
        PendingIntent pi = PendingIntent.getService(context, 0, in, 0);
        aManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,24 * 60 * 60 * 1000, pi);

记录几个详解文章
https://juejin.im/entry/588628e8128fe10065eb62a9
https://www.jianshu.com/p/32f438a0c239
https://blog.csdn.net/u012527802/article/details/70230110

2. WakeLock

WakeLock是Android框架层提供的一套机制,应用使用该机制可以达到控制Android设备状态的目的。这里的设备状态主要指屏幕的打开关闭,cpu的保持运行。简单的理解WakeLock是让系统保持"清醒"的一种手段

PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(
                                      PowerManager.PARTIAL_WAKE_LOCK
                                      | PowerManager.ON_AFTER_RELEASE,TAG);
wl.acquire();//为了保证任务不被系统休眠打断,申请WakeLock
// 开始我们的任务
wl.release();//任务结束后释放,如果不写该句。则可以用wl.acquire(timeout)的方式把释放的工作交给系统。

讲解https://www.jianshu.com/p/67ccdac38271

3. jobScheduler

google在全新一代操作系统上,采取了Job (jobservice & JobInfo)的方式,即每个需要后台的业务处理为一个job,通过系统管理job,来提高资源的利用率,从而提高性能,节省电源
Job Scheduler是将多个任务打包在一个场景下执行,当条件满足时采取执行.
使用过程讲解https://www.jianshu.com/p/55e16941bfbd
使用过程讲解 https://www.jianshu.com/p/9fb882cae239
源码讲解http://gityuan.com/2017/03/10/job_scheduler_service/

4. SyncAdapter

SyncAdapter是一个系统服务,通过系统的定时器更新应用程序数据ContentProvider,因为sync服务工作在独立进程,并且有操作系统歹毒,进程属于核心级别,系统不会杀掉,而使用了SyncAdapter的进程优先级本身也会提高,从而降低应用进程被杀的概率,
https://www.jianshu.com/p/dc9a2693478e
https://invoker.me/android-sync-adapter/

上一篇 下一篇

猜你喜欢

热点阅读