如何提升Android下内存的使意图识和排查能力?

  • 时间:2018-12-14 23:10 作者:云栖科技快讯 来源:云栖科技快讯 阅读:704
  • 扫一扫,手机访问
摘要:内存问题一直是大型App的开发人员比较头痛的问题,特别是像手淘这种超级的App,App中四处都是带有图片和视频的界面,而且这些功能都是由不同的团队甚至不同的事业部开发的,要整体上去管控和排查内存的问题变得相当的复杂。之前,我们多个线上版本都存在着严重的Activity等内存泄漏和不正当内存使用。这不
如何提升Android下内存的使意图识和排查能力?

内存问题一直是大型App的开发人员比较头痛的问题,特别是像手淘这种超级的App,App中四处都是带有图片和视频的界面,而且这些功能都是由不同的团队甚至不同的事业部开发的,要整体上去管控和排查内存的问题变得相当的复杂。之前,我们多个线上版本都存在着严重的Activity等内存泄漏和不正当内存使用。这不是偶然,一个很重要的起因就是我们很多的开发测试人员侧重业务开发,忽略内存和性能,而且没有站在全局性的角度去考虑资源的使用。认为我自己的板块多缓存少量就会加快速度,以空间换时间看似正确,但是在手淘这样的超级App中是不可取的,需要严格限制,否则不要说里面几百个板块,有几十个板块这样来做,其结果都会是灾难性的,不但没有加快速度,反而会拖慢速度以及带来很多稳固性问题。

经过一年多的升级,现在的Android Studio所带的工具已经相当的成熟。以前我们还停留在使用MAT来分析内存,但是现在Android Studio的内存分析工具已经相当的强大,已经完全可以抛开MAT来实现更为直观的内存检测。我想,作为一个大型App的开发人员和测试人员,掌握这些技能都是必不可少的,也是提高整个App质量的关键所在。

在使用工具分析内存之前,我们需要理解一下内存回收上的少量策略,否则很多时候排查到的可能都不是真正的问题。

1、没有强引用不代表这块内存就会马上回收

我们知道,Java的内存回收假如有强引用存在,那么这个对象的内存不会回收。那么这个对象的引用假如不存在,是不是这块内存就会回收呢?答案能否定的,VM有自己的回收策略,不会由于一个对象的引用为空了就立马对它进行回收。也就是说,回收策略需要达到肯定的触发阈值。我们可以看一个Demo,写如下的分配对象的方法:

如何提升Android下内存的使意图识和排查能力?

在内存充足的情况下,我们点击按钮4次,执行了4遍该函数,这个时候可以看到堆内存呈现了4次增长。如下图所示:

如何提升Android下内存的使意图识和排查能力?

静置在那半个小时,内存依然会维持现状,VM并不会来执行实际的GC。我们可以Dump内存看看内存中的对象:

如何提升Android下内存的使意图识和排查能力?

我们可以看到,4000个ImageView对象依然毫发无损的在内存中残留,系统没有回收其内存。不论是Dalvik还是ART环境,包括最新的Android N,都可能出现这样的情况,具体能否每次都保持(等量)增长等还要看手机内存剩余情况和手机的GC策略。所以,我们在检测内存占用或者者内存泄漏之前,肯定要执行工具自带的GC触发功能,否则结果会不精确。明明没有泄漏或者者没有占用,而Dump出来的堆中提醒占用很大或者者泄漏。 通过Memory Monitor工具,我们可以看到其引用的情况。点击其中一个ImageView,我们看下它的引用情况:

如何提升Android下内存的使意图识和排查能力?

可以看到,其引用路径是被一个FinalizerReference所持有。该对象已经标记为红色,表明我们自己的代码中已经没有其余引用持有该对象,状态是等待被回收。

通过手动触发GC,我们可以来主动回收这块内存,点击如下图所示的按钮,触发GC。

如何提升Android下内存的使意图识和排查能力?

当然,这里还有一个问题存在,由于刚才创立的属于Finalizer对象,该对象前面的文章已经分析过,需要至少两次GC才能真正回收其内存。所以,第一次触发GC的时候,我们可以看一下内存的变化。

如何提升Android下内存的使意图识和排查能力?

第一次触发GC后,内存只是部分的下降,这个时候Finalize链表中的对象被回收,但是ImageView还没有回收(在不同Rom和Android 版本下可能会存在少许差异,在Dalvik下的差异会更大少量)。我们可以看一下堆内存:

如何提升Android下内存的使意图识和排查能力?

该对象的引用链路一律标记为红色,已经没有强引用指向该对象,刚才的FinalizerReference已经执行了清除。但是第一次GC只是执行了Finalizer清除,而没有真正的回收这部分对象。所以还需要再一次的触发GC,再次执行GC后,我们可以看到堆内存下降,这些对象被回收了。

如何提升Android下内存的使意图识和排查能力?

我们可以通过Dump堆内存来验证能否已经回收,如下图所示,对内存中已经没有了刚才的ImageView对象,的确已经被回收了。

如何提升Android下内存的使意图识和排查能力?

通过前面的分析我们可以知道,并不是引用释放了,内存就会回收。在实际使用手机淘宝的过程中,我们也可以观察堆内存的变化,在退出一个界面的时候很可能是不会有GC的,堆内存不会变化。假如这个时候用MAT工具去Dump内存,那结果很多是不够正确的。而假如后续再做其余操作,引发GC后,才会使得结果更加精确,当然我们可以手动的去触发GC。

2、调用GC时的策略变化

在前面的文章中已经提到过,代码调用GC,只是告诉VM需要GC,但是能否真正的去执行GC还是要看VM的配置和能否达到阈值。前面也提到,在执行手动GC的时候,Dalvik和ART下会有比较显著的差异。Android 5.0开始添加了更多的GC方式,到了6.0,7.0在GC方面有了更多的优化。特别是在执行Finalizer对象方面,Android 5.0开始回收就没有那么快,单次执行GC,并不会导致失去引用的Finalizer对象进行完全回收,假如要更好的回收Finalizer对象,需要执行System.runFinalization()方法来强制回收Finalizer对象。

我们将测试代码后面加上主动调用GC的代码,如下:

如何提升Android下内存的使意图识和排查能力?

在不同的Android版本上看下执行效果,点击按钮执行屡次。在Android 4.4.4版本的设施上,内存基本已经回收,假如将1000次修改为10次,偶尔可以看到有ImageView已经没有引用存在,但是依然没有回收。如下图所示:

如何提升Android下内存的使意图识和排查能力?

从这里可以看出来,在该版本下,大部分的对象在没有强引用后,调用System.gc()就会被回收。我们再看下ART上的情况,在ART下,却发生了不一致的体现。在调用后没有进行GC。查看堆内存,我们也可以看到,4000个ImageView依然存在,并未执行GC。

如何提升Android下内存的使意图识和排查能力?

看来在ART后,这部分的GC策略做了调整,System.gc()没有内存回收。我们可以看下源码,在Android 4.4.4下的源码:

如何提升Android下内存的使意图识和排查能力?

我们可以看到,在调用gc函数后,直接调用了运行时的gc。再来看下5.0上的代码:

如何提升Android下内存的使意图识和排查能力?

如何提升Android下内存的使意图识和排查能力?

从源码我们可以发现,System.gc()函数,已经有了变化,从5.0开始,多了少量判断条件。能否执行gc,是依赖于justRanFinalization变量,而变量justRanFinalization在runFinalization后才会变为true。也就是说,直接调用System.gc()方法并没有调用Runtime.getRuntime().gc(),只是做了一个标记将runGC变量设置为true。

在runFinalization函数中,假如标记了runGC的,会先执行一次gc,而后清除Finalizer对象,并标记为依据清除过了Finalizer对象。这样在下次gc调用的时候,就会真正执行一次gc,以回收Finalizer对象。

在Android ART的设施上,我们将调用gc的方式做下变更:

如何提升Android下内存的使意图识和排查能力?

这里直接调用了Runtime.getRuntime().gc()。这个时候内存回收的确会有变化。但是Finalizer对象依然可能存在,在Android 5.0的时候会回收一部分,但是从6.0开始,单次调用gc,Finalizer对象却不肯定回收。如下图所示,尽管所有引用链路已经不复存在,但是内存依然没有回收:

如何提升Android下内存的使意图识和排查能力?

那么执行2次gc能否就都能把内存回收了呢?我们修改下代码:

如何提升Android下内存的使意图识和排查能力?

这里连续2次调用了gc,按理是可以回收Finalizer对象的,但是因为两次调用gc的间隔太短,而Finalizer对象是由专门的线程执行回收的,所以也不肯定能完全回收。这个和线程的调度情况有关系。例如执行上面代码可能出现的结果是部分回收:

如何提升Android下内存的使意图识和排查能力?

假如想要一律回收,可以在中间停顿少量间隔,或者者添加System.runFinalization()方法的调用。这样就能将当前可以回收的内存基本都回收了。我们在Android Studio的触发GC的按钮,也是通过BinderInternal$GcWatcher等代码来执行内存回收的。当然,在实际的业务代码中,不要主动调用gc,这样可能会导致额外的内存回收开销,在检测代码中,假如需要检测内存,最好按照gc,runFinalization,gc的顺序去回收内存后,再做内存的Dump分析。

3、使用Android Studio的Memory分析内存占用

Memory Monitor工具比起MAT一个很大的优势就是可以非常简单的查看内存占用,而且可以迅速找到自己的板块所占用的堆内存,所以希望开发和测试人员都能够迁移到Android Studio所带的工具上来。如何查看内存占用?真的是非常的简单,而且可以找到很细小的内存占用情况。

例如,这里自己设置一个类,而后创立该类,代码如下:

如何提升Android下内存的使意图识和排查能力?

这里只是创立了一个该对象,但是也很容易可以跟踪到该对象的内存情况。通过Memory Monitor的【Dump Java Heap】按钮可以把当前堆内存显示出来,如下图所示:

如何提升Android下内存的使意图识和排查能力?

这里是默认查看方式,我们可以切换到以包名的形式查看。这样即可以很容易的找到我们自己的代码了。如下所示:

如何提升Android下内存的使意图识和排查能力?

切换查看方式后,我们可以很容易的就找到自己所写的代码内存占用情况。这里可以看到实例的个数,占用内存的大小等情况。 例如在打开手机淘宝,简单操作一会后再来观察内存占用情况。按照包名查看后,我们很容易即可以看到整个堆内存的占用情况,如下图所示:

如何提升Android下内存的使意图识和排查能力?

通过上图我们很容易看到,在com.taobao包名下占用了近240多M的内存。继续往下看,聚划算板块,图片库板块占了大头。点击ju板块,开展后,又可以看到该包名下的内存占用:

如何提升Android下内存的使意图识和排查能力?

通过上图我们可以清晰的看到,在ju包名下的内存分布情况。

所以,在内存占用的检查上,Android Studio已经给我们提供了强大的工具,各个开发和测试人员已经可以很简单的查看到自己开发的板块占据的内存大小,而后对内存的使用做少量改进等措施。这里也可以通过右侧的Instance窗口检查各个实例的引用以及排查不正当的内存强制占用。

4、排查内存泄漏

Memory Dump下来后,我们可以检查Java堆的Activity内存泄漏和重复的String。很多人还习惯于MAT分析工具,其实Memory Monitor已经包含了这个功能,而且使用非常简单。 首先dump内存,如前面分析的那样,在右侧可以看到【Analyzer Tasks】按钮。

如何提升Android下内存的使意图识和排查能力?

而后,我们点击该按钮,即可以看到分析泄漏的工具。

如何提升Android下内存的使意图识和排查能力?

这里可以只勾选 检测泄漏Activity选项,而后选择执行。这样即可以看到泄漏的Activity了。

如何提升Android下内存的使意图识和排查能力?

通过引用指向,我们可以比较容易的判断出不该持有的强引用关系,而且该工具从上到下的排序,已经做了初步的判断。

当然在检测泄漏和占用之前,需要点击2次GC的按钮,这样结果才会相对精确。

对于Android Studio提供的内存分析工具,使用起来非常简单,会比Mat工具要快捷,排查问题也更加容易。所以Android的开发和测试人员,应该尽可能的都迁移到该工具上来,并能够熟练的掌握内存分析工具,这样才能让自己开发的板块质量更加的优秀。

以上主要是针对Android Studio 2中的使用方式,在今年的Android Studio 3 Preview版本中,内存这块的分析工具更增强大,可以在面板上直接看到更细粒度的内存占用,不仅仅是Java的对内存了。

如何提升Android下内存的使意图识和排查能力?

对于需要更细粒度和更全面的分析少量内存的细节,本文所涉及的内存知识还是不够的,还需要理解Linux下的内存机制以及Android下的少量内存机制,例如按页分配,共享内存,GPU内存等等。

更多资讯,尽在阿里云科技快讯~

来科技快讯看新闻鸭~

快点关注我认识我爱上我啊~~~

如何提升Android下内存的使意图识和排查能力?

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
【系统环境|】ZORRO佐罗软件安装教程及一键新机使用方法详细简介(2023-02-10 21:56)
【系统环境|】阿里云 centos 云盘扩容命令(2023-01-10 16:35)
【系统环境|】补单系统搭建补单源码搭建(2022-05-18 11:35)
【系统环境|服务器应用】高端显卡再度登上热搜,竟然是因为“断崖式”的降价(2022-04-12 19:47)
手机二维码手机访问领取大礼包
返回顶部