Android性能优化(二)内存优化
上一篇文章,我总结了一下app启动优化的一些知识。这篇文章,总结一下内存优化相关的一些知识。内存优化,相比于启动优化,可能没那么明显。为什么这么说呢?启动快慢,我们体验一次就能体验出来。而内存增长,我们操作一次,两次,三次......如果不借助什么工具或者命令的话,我们的肉眼并不能发现什么。因此,在很多情况下,我们会忽视掉app内存这一块。可能大部分程序员,知道自己的app存在内存问题,往往是因为QA的monkey test中,存在着某个oom的崩溃,或者bugly上频繁上报oom的崩溃。那这个时候,我们意识到,原来我们的app并没有想象中那么完美。
一、前言
在正式的总结内存优化相关知识之前,我还是说一些我自己的经历。如下图所示,在这个照片中,记录的是我以前参与开发的一个功能。当我们自信心满满地填完测试用例,提测后,发现没什么大的问题。然后,QA下班跑monkey,第二天一大早,各种crash日志打包给你发过来了。打开一看,啥,我的应用跑了一晚上monkey内存增长了400M?我靠,这么牛逼!我不信,我试试。于是,我就试了试,下图就是我当年的记录:
怎么样?你跌倒了没?崩溃了没?崩溃了,彻底崩溃了。尤其以前我们做手机,非常强调整体的内存。于是,我们抓紧联系QA:小姐姐,这个问题不大,我们一会就修复好了。说完,立马去查看代码。这块内存释放了吗?释放了。这里的对象回收了吗?回收了。这个容器清空了吗?清空了。好像没问题啊!!!但是,你自己的测试结果都在那!!!所以,我们回归到那句话,内存问题,不像是启动问题或者是一些直观的UI问题,往往容易被忽视。而且,根据代码去排查根本不好排查。一个是因为代码就是我们自己写的,再一个就是我们不可能一行一行代码去找,我们只能大体的去找一些回收函数,或者猜测可能出问题的模块。
二、三种内存问题
1、内存抖动
记得刚工作的时候,我做好自己的工作,就会去看我导师和我主管头上挂着的BUG。记得在我主管那看到一个性能组的BUG:XXX模块XXX操作时,存在卡顿,内存图如下。我看了一下,内存图这不是很正常吗,有增加有减少,整体内存没有太大的增长啊。我于是就得出了结论:没问题。于是,我继续往下滑,看到了我主管的comment:内存抖动。啥?内存抖动?这是个啥?于是,我百度了一下,大体了解到:
内存抖动,由于短时间内频繁的创建和销毁对象,频繁触发GC操作导致的内存问题。对象创建并用完后及时销毁,这是没问题的。但是如果频繁的创建和销毁,就会频繁的触发GC操作。其实,GC操作是非常消耗CPU的,进而会导致卡顿等问题。
我写个demo模拟下内存抖动的情况,我在界面上放了两个Button,一个叫创建,一个叫销毁,点击创建会创建一个bitmap,点击销毁则销毁该bitmap。代码如下:
button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = false;mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);Log.d("TTTT","create");}});button2.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mBitmap != null && !mBitmap.isRecycled()) {Log.d("TTTT","recycle");mBitmap.recycle();mBitmap = null;}}});
我们使用profiler监控一下内存变化,放大后,如下图所示。很明显,内存增减交错,呈现锯齿状,但由于我们有创建,有回收,整体的内存没有大幅度的增加:
2、内存泄漏
接下来,说一下什么是内存泄漏。内存泄漏,简单来说,就是内存有申请,没释放,或者说没有完全释放,导致可用内存逐渐减少,当到达一定的程度时,也会频繁的触发GC操作。因此,内存泄漏也会导致卡顿等性能问题。
我依然通过一个小例子,演示一下内存泄漏。我还是使用上面的例子稍微改一下。我每次点击创建都会创建一个不同大小的Bitmap。而且,我只点创建,不点销毁,代码如下:
button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = false;mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);size++;Bitmap bitmap = Bitmap.createScaledBitmap(mBitmap, 54 * size, 96 * size, false);Log.d("TTTT", "create");}});
我们使用profiler抓一下内存,我们可以看到,随着我一直点击创建,内存一直在增加,虽然也有骤降的地方,但是整体趋势是增加的。从64M一直增加到了190M,且没有停止的趋势:
3、内存溢出
内存溢出,我接着上面的内存泄漏来说。我们从内存泄漏可以知道,内存一直申请一直增加,没有释放的趋势。当达到Android系统为每个app设置的内存阈值后,会如何?毫无疑问,会因为oom(OutOfMemoryError),导致崩溃。
首先,我们在程序中获取一下内存阈值,很简单的代码:
Runtime runtime = Runtime.getRuntime();
Log.d("TTTT", "maxMemory:" + runtime.maxMemory());
看一下打印的结果:
2020-03-07 22:19:24.926 4482-4482/? D/TTTT: maxMemory:201326592
我们计算一下201326592子节是多少M:201326592/(1024^2) = 192M。也就是说,我当前手机的内存阈值是192M。当然,我们有方法可以将内存扩展为更大,在这里我先不多说。
事实上,在上面的代码中,我已经点到崩溃了,那我接着上面的图,把后面的内存图贴一下。如下所示,可以看到,在内存在128M-192M之间的时候,已经开始频繁的触发GC操作。再往后,我们发现,内存直接降到了0,不动了。为什么?因为,他真的达到内存阈值了,他真的崩溃了。
我们把鼠标移动到图的某一帧位置,可以查看到此时的内存情况,可以看到,最后崩溃前,JAVA内存占用达到了186M,总内存达到233.6M。而我们app刚启动的时候,JAVA内存只有6M,总内存46.8M,如下图所示:
我们看一下崩溃日志,果不其然,分配内存失败,OOM:
三、内存优化
首先,在总结内存优化的方法前,我们先总结一下几种内存问题的原因:
(1)对象频繁创建和销毁导致内存抖动,触发GC操作,导致卡顿甚至OOM
(2)只申请内存,没有释放内存,导致系统可用内存减少,触发GC操作,导致卡顿直至OOM
因此,内存优化的两个方向就是:避免内存抖动和内存泄漏。那么,如何避免内存抖动和内存泄漏呢?其实,我们在上面的demo中,做了两种错误的操作。我们也可以据此给出几种避免内存抖动和内存泄漏的方法。由于,内存优化的方法太多了,而且需要根据具体的项目,我们不一一展开。在此,我总结一下我用过的以及以前从资料上查到的一些优化技巧。大家可以据此考虑一下自己的项目中,是否存在某些地方不合理。
1、Bitmap的使用
大家可以看到,我的demo中使用的是Bitmap来演示一些错误的代码。其实,对于大多数内存泄漏问题,都跟Bitmap有关系。因为。这玩意如果使用不当,将会带来非常大的内存消耗。可能大家现在展示图片基本都使用Glide等第三方库,很少去使用BitmapFactory去解码Bitmap。但是,对于Bitmap的使用,我还是重点总结一下:
(1)使用BitmapFactory来decode一块比较大的Bitmap时,需要注意inSampleSize的计算。通常,我使用的方法是:
- 设置inJustDecodeBounds为true,可以在不解码Bitmap的情况下获取Bitmap的实际宽高(sourceWidth,sourceHeight);
- 选取图片显示区域的宽(objWidth)或高(objHeight)作为参考值,根据fitin算法,计算合适的inSampleSize;
- 选取合适的解码位数,32位(ARGB8888),16位(RGB565)等,如果没有特殊需求,使用RGB565即可;
- 根据计算得到的inSampleSize,设置inJustDecodeBounds为false,解码Bitmap。
(2)Bitmap使用完后,及时的回收,回收的方法如下:
if (mBitmap != null && !mBitmap.isRecycled()) {mBitmap.recycle();mBitmap = null;}
2、static对象谨慎使用
3、cursor及时关闭
4、各种监听器的注销
5、SharedPreferences谨慎使用
6、第三方库谨慎使用
总结一下,这篇博客从我的亲身经历出发,介绍了三个内存问题及其原因。并根据导致内存问题的几个原因,提出了一些内存优化的建议和方向。当然,我以前接触比较多的就是Bitmap,因此我花了较多的篇幅围绕Bitmap来写demo和提出建议。后面,我会逐渐的丰富和总结其他的一些内存优化注意事项。
Android性能优化(二)内存优化相关推荐
- Android 系统性能优化(55)---Android 性能优化之内存优化
Android 性能优化之内存优化 前言 Android App优化这个问题,我相信是Android开发者一个永恒的话题.本篇文章也不例外,也是来讲解一下Android内存优化.那么本篇文章有什么不同 ...
- App性能优化(布局优化,线程优化,app瘦身优化,页面切换优化,App启动优化,内存优化)
Android APP性能优化(最新总结) 在目前Android开发中,UI布局可以说是每个App使用频率很高的,随着UI越来越多,布局的重复性.复杂度也随之增长,这样使得UI布局的优化,显得至关重要 ...
- Lua性能优化—Lua内存优化
原文链接https://blog.uwa4d.com/archives/usparkle_luaperformance.html 这是侑虎科技第236篇原创文章,感谢作者舒航供稿,欢迎转发分享,未经作 ...
- android—性能优化2—内存优化
文章目录 性能优化: 工具: memory profiler LeakCanary arthook epic 库 java内存管理机制 java 内存回收机制 Android内存管理机制 Dalvik ...
- Android性能优化之内存优化 1
导语 智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是Android阵营,从一开始的一两百M到今天动辄4G,6G内存.然而大部分的开发者观看下自己的异常上报系统,还是 ...
- Android 性能监测工具,优化内存、卡顿、耗电、APK的方法
导语 安卓大军浩浩荡荡,发展已近十个年头,技术优化月新日异,如今 Android 9.0 代号P 都发布了,Android系统性能已经非常流畅了.但是,到了各大厂商手里,改源码自定系统,使得 ...
- Android面试-Android性能优化和内存优化、APP启动速度一线大厂的实战案例解析
一.Android 内存管理机制 二.优化内存的意义 三.避免内存泄漏 四.优化内存空间 五.图片管理模块的设计与实现 六.总结 深入探索Android内存优化 第一章.重识内存优化 第二章.常见工具 ...
- Android性能优化之内存优化浅析
一.背景 Android由于是以Java语言为主要开发语言,所以它的内存管理并不像C语言那样由开发者去管理内存的分配以及回收等,而是交由JVM虚拟机的内存回收机制去处理.这就导致我们在开发过程中难免会 ...
- Android App性能优化之内存优化
为什么要进行内存优化? 1.App运行内存限制,OOM导致App崩溃 2.App性能:流畅性.响应速度和用户体验 Android的内存管理方式 Android系统内存分配与回收方式 ● 一个App ...
- Android 系统性能优化(30)---Android性能全面分析与优化方案研究
Android 性能优化 1.结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 2性能问题分类 1.渲染问题:过度绘制.布局冗杂 2.内存问题:内存 ...
最新文章
- mybatis连接mysql数据库连接池_对于数据库连接池的一些思考和MyBatis的集成与使用...
- 倾斜——让设计更有冲击力
- java中为什么需要常量和变量的区别_Java中的变量和常量
- Deep learning:二十二(linear decoder练习)
- Huggingface简介及BERT tansformer 开源
- JS 获取控件的绝对位置
- rac下asm管理的表空间-数据文件的重命名
- oc-14-对象方法调用类方法
- ant+jmeter
- C语言 float、double数据在内存中的存储方式
- Listview 的应用 Day04 2014-0605
- css如何改变横线<hr/>标签的颜色
- java IO流分类
- Java //PP2.14 按照以下要求修改程序Snowman: ·在其身上添加两个红色按钮。·将雪人的表情由笑脸变成皱眉。·把太阳移动到图片的右上角。·在图片左上角显示你的名字。。。
- 2017-2018年终总结书
- Ansys Lumerical | 行波 Mach-Zehnder 调制器仿真分析
- ETCD 源码学习--Raft 选举的 Quorum 机制实现(七)
- c语言写字机器人,写字机器人(基于STM32简易实现)
- js定义函数的两种形式及区别
- P1827 [USACO3.4] 美国血统 American Heritage