上一篇文章,我总结了一下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性能优化(二)内存优化相关推荐

  1. Android 系统性能优化(55)---Android 性能优化之内存优化

    Android 性能优化之内存优化 前言 Android App优化这个问题,我相信是Android开发者一个永恒的话题.本篇文章也不例外,也是来讲解一下Android内存优化.那么本篇文章有什么不同 ...

  2. App性能优化(布局优化,线程优化,app瘦身优化,页面切换优化,App启动优化,内存优化)

    Android APP性能优化(最新总结) 在目前Android开发中,UI布局可以说是每个App使用频率很高的,随着UI越来越多,布局的重复性.复杂度也随之增长,这样使得UI布局的优化,显得至关重要 ...

  3. Lua性能优化—Lua内存优化

    原文链接https://blog.uwa4d.com/archives/usparkle_luaperformance.html 这是侑虎科技第236篇原创文章,感谢作者舒航供稿,欢迎转发分享,未经作 ...

  4. android—性能优化2—内存优化

    文章目录 性能优化: 工具: memory profiler LeakCanary arthook epic 库 java内存管理机制 java 内存回收机制 Android内存管理机制 Dalvik ...

  5. Android性能优化之内存优化 1

    导语 智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是Android阵营,从一开始的一两百M到今天动辄4G,6G内存.然而大部分的开发者观看下自己的异常上报系统,还是 ...

  6. Android 性能监测工具,优化内存、卡顿、耗电、APK的方法

    导语     安卓大军浩浩荡荡,发展已近十个年头,技术优化月新日异,如今 Android 9.0 代号P  都发布了,Android系统性能已经非常流畅了.但是,到了各大厂商手里,改源码自定系统,使得 ...

  7. Android面试-Android性能优化和内存优化、APP启动速度一线大厂的实战案例解析

    一.Android 内存管理机制 二.优化内存的意义 三.避免内存泄漏 四.优化内存空间 五.图片管理模块的设计与实现 六.总结 深入探索Android内存优化 第一章.重识内存优化 第二章.常见工具 ...

  8. Android性能优化之内存优化浅析

    一.背景 Android由于是以Java语言为主要开发语言,所以它的内存管理并不像C语言那样由开发者去管理内存的分配以及回收等,而是交由JVM虚拟机的内存回收机制去处理.这就导致我们在开发过程中难免会 ...

  9. Android App性能优化之内存优化

    为什么要进行内存优化? 1.App运行内存限制,OOM导致App崩溃 2.App性能:流畅性.响应速度和用户体验 Android的内存管理方式 Android系统内存分配与回收方式 ●   一个App ...

  10. Android 系统性能优化(30)---Android性能全面分析与优化方案研究

    Android 性能优化 1.结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 2性能问题分类 1.渲染问题:过度绘制.布局冗杂 2.内存问题:内存 ...

最新文章

  1. mybatis连接mysql数据库连接池_对于数据库连接池的一些思考和MyBatis的集成与使用...
  2. 倾斜——让设计更有冲击力
  3. java中为什么需要常量和变量的区别_Java中的变量和常量
  4. Deep learning:二十二(linear decoder练习)
  5. Huggingface简介及BERT tansformer 开源
  6. JS 获取控件的绝对位置
  7. rac下asm管理的表空间-数据文件的重命名
  8. oc-14-对象方法调用类方法
  9. ant+jmeter
  10. C语言 float、double数据在内存中的存储方式
  11. Listview 的应用 Day04 2014-0605
  12. css如何改变横线<hr/>标签的颜色
  13. java IO流分类
  14. Java //PP2.14 按照以下要求修改程序Snowman: ·在其身上添加两个红色按钮。·将雪人的表情由笑脸变成皱眉。·把太阳移动到图片的右上角。·在图片左上角显示你的名字。。。
  15. 2017-2018年终总结书
  16. Ansys Lumerical | 行波 Mach-Zehnder 调制器仿真分析
  17. ETCD 源码学习--Raft 选举的 Quorum 机制实现(七)
  18. c语言写字机器人,写字机器人(基于STM32简易实现)
  19. js定义函数的两种形式及区别
  20. P1827 [USACO3.4] 美国血统 American Heritage

热门文章

  1. grpc实现流量染色
  2. 游戏设计的艺术:一本透镜的书——第十九章 世界包含着各种空间
  3. c语言float m1 m2什么意思,M0、M1、M2的涵义及其作用
  4. 一张图搞懂什么是M0、M1 、M2
  5. 学计算机方面该怎样保养眼睛,电脑一族如何保护眼睛
  6. java的一些学习网址。。。。。
  7. InoReader——网页无法打开
  8. 银耳椰椰——Alpha冲刺Day04
  9. 切换IE浏览器的版本
  10. Codeforces1196D2