该篇笔记来自于平时学习时,对各种学习资源的整合,如有冒犯敬请谅解,整理的不好,还望指出错误,主要用于查找与记录
一、内存泄露
针对内存泄露我认为要知道下面三点:
第一:要弄清楚内存泄露与内存溢出的区别
第二:要弄清楚常规的内存分析方法,重点掌握Leakcanary的使用和原理
第三:要清楚内存泄露出现的常规场景与解决办法
1)------ 什么是内存泄露,什么是内存溢出,它两之间的关系?
内存泄露是指程序在申请了内存后,无法释放已申请的内存空间。(小)
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用。 (大)
它两之间的关系:
内存的溢出是内存分配达到了最大值,而内存泄漏是无用内存充斥了内存堆;因此内存泄漏是导致内存溢出的元凶之一,而且是很大的元凶;因为内存分配完后,哪怕占用再大,也会回收,而泄漏的内存则不然;当清理掉无用内存后,内存溢出的阀值也会相应降低。
描述说明:
有一块内存,内存里面有很多个对象,在内存回收时,有些对象不能被回收,就成了垃圾对象,即没得用了的对象,这个垃圾对象就是我们所说的内存泄漏,当垃圾对象一直在累加到内存的最大值时,就会造成内存溢出,内存溢出的元凶就是内存泄露。
2)------ 找出内存泄露的方法
&&1、用MAT工具进行分析
&&2、使用studio 自带的Memory Monitor工具
&&3、使用StrictMode (严格模式)
&&4、集成Leackcanary工具
@@1. MAT 分析工具 (可详看 android群英传 244页 前后几页)
https://www.eclipse.org/mat/ 下载对应的文件
由于我们内存泄漏一般发生在Activity中,因此只需要查找Activity即可。
点击下图中标记的QQL图标 输入
select * from instanceof android.app.Activity
类似于 SQL语句 查找 Activity相关的信息 点击 红色叹号执行后 如下图所示:
我们可以看到过滤到的Activity信息, 其中内存中还存在 3个SecondActivity实例,但是我们是想要全部退出的,这表明出现了内存泄漏其中 有 Shallow size 和 Retained Size两个属性
Shallow Size :对象自身占用的内存大小,不包括它引用的对象。针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。针对数组类型的对象,它的大小是数组元素对象的大小总和。
Retained Size:Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用)不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。
接下来 右击一个SecondActivity 选择 with all references
对红色框圈中的 信息,进行分析出现的泄露原因:
下面这张图是来自网络,他分析出来的信息如下:
(感谢这位作者,文章有参考他的内容)https://blog.csdn.net/u012760183/article/details/52068490
@@2.studio自带的Memory monitor工具(推荐)
@@3. 使用StrictMode (严格模式)
最新的Android平台中(Android 2.3起),新增加了一个新的类,叫StrictMode(android.os.StrictMode)。这个类可以用来帮助开发者改进他们编写的应用,并且提供了各种的策略,这些策略能随时检查和报告开发者开发应用中存在的问题,比如可以监视那些本不应该在主线程中完成的工作或者其他的一些不规范和不好的代码。
StrictMode有多种不同的策略,每一种策略又有不同的规则,当开发者违背某个规则时,每个策略都有不同的方法去显示提醒用户。 详细请看:https://www.jianshu.com/p/113b9c54b5d1
@@4.Leakcanary工具 (也是重重之重)
使用方法:
##1.gradle 配置 依赖
##2. 初始化
##3.使用refwatcher 的watch方法进行页面监控
##4.manifest加权限
##5. 运行出现泄漏,会出现 下面 附属黄色图标app,同时会有通知,详情指出溢出信息。
我的由于在模拟器上面,没有成功运行出来,可详看 第2个图片。
基本原理:
- RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
- 然后在后台线程检查引用是否被清除,如果没有,调用GC。
- 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
- 在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
- 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
- HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
- 引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。
拓展: Leakcanary的自定义 和 上线过程中的泄露信息上传。
3)------ 哪些操作会导致内存泄漏:
##1.static 静态变量导致的内存泄漏
例如:
1.控件前声明时使用static 导致释放不掉而产生内存泄露
2.内置类声明时使用static 导致释放不掉而产生内存泄露
...........................
##2.单例模式导致的内存泄漏
##3.属性动画造成的内存泄漏
##4.线程操作不当也会造成内存泄漏
##5. hadle 操作不当 ,hadle 释放不了就会造成内存泄露
handle 的解决办法:
##6. 定时器操作不当也会导致内存泄露
##7.webview 会导致内存泄露 ,解决办法,开单独线程
##8.第三方库使用不当致内存泄露 ,解决办法,开单独线程
对于EventBus,RxJava等一些第三开源框架的使用,若是在Activity销毁之前没有进行解除订阅将会导致内存泄漏。
##9. 资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
二、各种性能优化
针对性能优化我认为也有3点:
第一:要清楚,为啥要进行性能优化
第二:要清楚,性能优化能从哪些方面做起,自我归结为三级优化
第三:要清楚,形成性能优化的技能的过程,(常规抓起,遇到问题用于解决,形成技能沉淀)
1)------ 为什么要进行性能优化
程序优化不好,会造成程序卡顿,无响应 ,耗电,oom,甚至会时常崩溃。
这对用户来说,只有一种可能结果,用户会直接卸载你的app.
当然对于我们程序员来说,这是我们必备的技能,也是我们与其他程序员能拿开差距的地方。
2)------ 性能优化都能从哪些地方做起
&&1.代码和布局优化 (起步:一级)
&&2.针对卡顿,无响应,内存 做优化 (必备基本功:二级)
&&3.针对专门领域逐个击破(降龙十八掌:三级):
列表控件(listview和 recyleview)优化、图片bitmap优化、线程优化(线程池)、网络优化、sql数据库优化(速度,使用技巧)
电量优化、app启动优化
@@1.代码优化 (知道准则,会用lint,懂修改,写代码时养成一些好的习惯)
代码优化的准则:
1. 不要做沉余的工作。
2.如果能避免,尽量不要做过多的进行内存分配操作
3.要深入的理解所用语言的特性和了如指掌的达到全面了解anroid sdk 提供的api。
lint(冷特)工具的使用:
命令的使用:
用右边gradle :
lint 图形化 界面操作:
https://blog.csdn.net/u011240877/article/details/54141714#%E4%BB%80%E4%B9%88%E6%98%AF-lint
具体优化场景:
1.正确选择合适的数据结构。
2.掌握handler的正确使用方法
两种方案:
3.正确的使用Context,区别对待Context
4. 掌握java的四种引用方法:强引用,弱引用,软引用,虚引用
5、代码微优化: https://linux.cn/article-6218-1.html(Android 代码性能优化建议)
@@2.布局优化(能用三大标签的,就常用三大标签)
减少布局层次,降低View树
<include/>
<merge/>
<ViewStub/>
详细文章:https://www.jianshu.com/p/ee9e4b8cb95f (布局性能优化)
布局分析工具:
##1.Layout Inspector
##2.ADM 自带工具
##3.使用 hierarchyViewer (详见android群英传 234页 前后)
@@3.卡顿 和无响应的优化:
##1.什么是16ms黄金准则?
##2.造成卡顿和无响应的根源是什么?
主要是布局复杂,不正当的操作,造成了渲染 问题,所以,才出现了卡顿和无响应。
gc 频繁回收,我们就称为 内存抖动。
对象池:
卡顿分析的方法:
traceView的使用:
https://blog.csdn.net/superxlcr/article/details/78219673 (TraceView工具如何使用)
https://www.cnblogs.com/sunzn/p/3192231.html(Android 编程下的 TraceView 简介及其案例实战)
SysTrace的使用:
https://www.jianshu.com/p/3a45dd9bd140 (Systrace简单使用方法)
https://www.jianshu.com/p/75aa88d1b575(Systrace 分析性能工具使用方法详解)
无响应:程序无响应 简称ANR,全称Application Not Responding
手机上显示的:较差的手机出现频率越高
在UI进程中,在规定时间内没有把活干完,就会弹出下面这个框:
例:一般肉眼是16毫秒换一次图片,有三种异常情况:
1.掉帧,就是直接飞到下一个。
2。卡顿 一般是流畅度的问题 ,如果16毫秒之后看不到下一张图片或变样子的话,比如到24毫秒才做完,那么就放到下一帧
3.如果长时间没有反应的话就会产生ANR这种情况。
ANR的类型
第一种:KeyDispatchTimeout:
主要类型,按键或触摸事件无响应(大部分产生ANR的情况)
避免KeyDispatchTimeout的办法:1.UI线程只做UI相关的事情
2.耗时的操作(IO、DB等)放到子线程
3.使用Handler来处理进程之间的切换
第二种:BroadcastTimeout(10 seconds) 广播超时
第三种:ServiceTimeout(20 seconds) 服务超时
超时原因:1.当前的事件没有机会得到处理
2.当前的事件正在处理,但没有及时完成
哪些是在UI线程中做的事情:
1.Activity的生命周期函数
2.AsyncTask的除doInBackground之外的函数
3.Handle的post和handleMessage方法
4.Service默认在主线程执行的
5.BR的onReceive回调函数
6.View的post方法
如何查看ANR 日志:
blockCanary的使用:https://blog.csdn.net/qq_29201493/article/details/80326297(APP卡顿检测工具 —— BlockCanary的集成)
(如果需要分析Native代码的耗时,可以选择Simpleperf;如果想分析系统调用,可以选择systrace;如果想分析整个程序执行流程的耗时,可以选择Traceview或者插桩版本的systrace )
@@4.专门领域,逐个击破,列表优化:
rv性能优化:https://www.jianshu.com/p/4809e1872f50
@@5.专门领域,逐个击破,图片bitmap的优化:
压缩图片,是图片优化中最为重要的一个手段。
压缩图片工具: 详见android 高级进阶 381 页。
Bitmap优化的策略总结为以下3种:
1.对图片质量进行压缩
2.对图片尺寸进行压缩
3.使用libjpeg.so库进行压缩。
https://blog.csdn.net/u012124438/article/details/66087785
@@6.专门领域,逐个击破,电量优化:
耗电大户:
android 高级进阶的383 页 ,讲的很详细,
主要是 广播,定位,闹钟,瞌睡模式。
@@7.专门领域,逐个击破,线程优化:(线程池的使用)
详见:开发艺术探讨,第406页
@@8.专门领域,逐个击破,网络请求优化:
android高级进阶,第37章,详解。
@@9.专门领域,逐个击破,Sql的优化:(速度,使用技巧)
@@10.专门领域,逐个击破,App启动优化:
优化思路总结
1、UI渲染优化,去除重复绘制,减少UI重复绘制时间,打开设置中的GPU过度绘制开关,各界面过度绘制不应超过2.5x;也就是打开此调试开关后,界面整体呈现浅色,特别复杂的界面,红色区域也不应该超过全屏幕的四分之一;
2、根据优先级的划分,KoMobileApplication的一些初始化工作能否将任务优先级划分成3,在首页渲染完成后进行加载,比如:PaySDKManager。
3、主线程中的所有SharedPreference能否在非UI线程中进行,SharedPreferences的apply函数需要注意,因为Commit函数会阻塞IO,这个函数虽然执行很快,但是系统会有另外一个线程来负责写操作,当apply频率高的时候,该线程就会比较占用CPU资源。类似的还有统计埋点等,在主线程埋点但异步线程提交,频率高的情况也会出现这样的问题。
4、检查BaseActivity,不恰当的操作会影响所有子Activity的启动。
5、对于首次启动的黑屏问题,对于“黑屏”是否可以设计一个.9图片替换掉,间接减少用户等待时间。
6、对于网络错误界面,友好提示界面,使用ViewStub的方式,减少UI一次性绘制的压力。
7、任务优先级为2,3的,通过下面这种方式进行懒加载的方式
- getWindow().getDecorView().post(new Runnable() {
- @Override
- public void run() {
- myHandler.post(mLoadingRunnable);
- }
- });
8、Multidex的使用,也是拖慢启动速度的元凶,必须要做优化。
三、记录张老师对优化的一些思路
1)------ 关于崩溃
崩溃率是衡量一个应用质量高低的基本指标。
android的崩溃分为 java崩溃 和 Native崩溃,java崩溃就是在java代码中,出现了未捕获异常,导致程序异常退出。
Native崩溃是因为Native代码中访问了非法地址,也可能是地址对齐出现了问题,或者发生了程序主动abort,这些都会产生相应 的signal信号,导致程序异常退出。
##1.Native崩溃的捕获流程和难点 (思路)
@@1. Native崩溃机制 : https://mp.weixin.qq.com/s/g-WzYF3wWAljok1XjPoo7w?
@@2.Native崩溃从捕获到解析流程
@@3.Native崩溃捕获的难点
Native崩溃捕获 处理方案:Breakpad(https://chromium.googlesource.com/breakpad/breakpad/+/master )
崩溃捕获各种平台:腾讯的Bugly、阿⾥的啄⽊⻦平台、⽹易云捕、Google的Firebase。
难点在于:怎么样保证客户端在各种极端情况下依然可以⽣成崩溃⽇志
棘⼿的情况:
情况⼀:⽂件句柄泄漏,导致创建⽇志⽂件失败,怎么办? 答:提前申请⽂件句柄fd预留,防⽌出现这种情况。
情况二:因为栈溢出了,导致⽇志⽣成失败,怎么办? 答:使⽤常⻅的signalstack,也需要在堆中预留部分空间。
情况三:整个堆的内存都耗尽了,导致⽇志⽣成失败,怎么办? 答:Breakpad 的 Linux Syscall Support
情况四:堆破坏或⼆次崩溃导致⽇志⽣成失败,怎么办?答:Breakpad
想彻底弄清楚Native崩溃捕获,需要我们对虚拟机运⾏、汇编这些内功有⼀定造诣。
##2.崩溃的计算和异常率
UV 崩溃率 = 发⽣崩溃的 UV / 登录 UV
UV 异常率 = 发⽣异常退出或崩溃的 UV / 登录 UV
##3.关于崩溃的认知:
做⼀个⾼可⽤的崩溃收集SDK并不容易,它背后涉及Linux信号处理以及内存分配、汇编等知识,当你内功修炼得越深厚,学习这些底层知识就越得⼼应⼿,崩溃率的⾼低跟应⽤时⻓、复杂度、收集SDK有关,作为技术⼈员,我们不应该盲⽬追求崩溃率这⼀个数字,应该以⽤户体验为先,如果强⾏去掩盖⼀些问题往往更加适得其反。
我们不应该随意使⽤try catch去隐藏真正的问题,要从源头⼊⼿,了解崩溃的本质原因,保证后⾯的运⾏流程。在解决崩溃的过程,也要做到由点到⾯,不能只针对这个崩溃去解决,⽽应该要考虑这⼀类崩溃怎么解决和预防。崩溃的治理是⼀个⻓期的过程(崩溃的⻓期保卫战)
2)------ 崩溃优化
##1.挖掘崩溃现场(第⼀案发现场),发现有价值的线索,挖掘到的信息越多越好,⽽不是去靠盲⽬猜测。崩溃现场应该采集哪些信息:
@@1.崩溃信息 (进程名、线程名,崩溃堆栈和类型)
@@2.系统信息 (应⽤、系统的运⾏⽇志,机型、系统、⼚商、CPU、ABI、Linux版本,设备状态)
@@3.内存信息 (⼿机内存分为“2GB以下”和“2GB以上”两个 桶,然后收集:系统剩余内存,应⽤使⽤内存,虚拟内存信息)
@@4.资源信息 (资源泄漏,⽂件句柄fd,线程数,JNI泄漏)
@@5. 应用信息 (崩溃发⽣在哪个Activity或Fragment,发⽣在哪个业务中,关键操作路径,其他⾃定义信息(⽐如⽹易云⾳乐会关注当前播放的⾳乐,QQ浏览器会关注当前打 开的⽹址或视频。此外例如运⾏时间、是否加载了补丁、是否是全新安装或升级等信息也⾮常重要))
@@6.我们可能还需要获取类似磁盘空间、电量、⽹络使⽤等特定信息。
⼀个好的崩溃捕获⼯具,会根据场景为我们采集⾜够多的信息,让我们有更多的线索去分析和定位问题。当然数据的采集需要 注意⽤户隐私,做到⾜够强度的加密和脱敏。
##2.应用 崩溃分析“三部曲”
@@1.第⼀步:确定重点 (确认严重程度,崩溃基本信息,Logcat日志,各个资源情况)
@@2.第⼆步:查找共性 (尝试查找这类崩溃有没有什么共性,哪些维度聚合)
@@3.第三步:尝试复现 (在稳定的复现路径上⾯,我们 以采⽤增加⽇志或使⽤Debugger、GDB等各种各样的⼿段或⼯具做进⼀步分析,达到解决问题)
针对系统崩溃怎么办:
@@@1. 查找可能的原因。通过上⾯的共性归类,我们先看看是某个系统版本的问题,还是某个⼚商特定ROM的问题。虽然崩溃⽇ 志可能没有我们⾃⼰的代码,但通过操作路径和⽇志,我们可以找到⼀些怀疑的点。
@@@2. 尝试规避。查看可疑的代码调⽤,是否使⽤了不恰当的API,是否可以更换其他的实现⽅式规避。
@@@3. Hook解决。这⾥分为Java Hook和Native Hook。
崩溃攻防是⼀个⻓期的过程,要想提前预防崩溃的发⽣,将它消灭在萌芽阶段,会涉及整个流程,包括⼈员的培训、编译检查、静态扫描⼯作,还有规范的测试、灰度、发布流程等。