程序性能优化之内存优化(三)上篇

点击绿色箭头,此时大家会看到Leaked Activities下有一个LaunchActvity@31…的信息,没错发生了内存泄漏,稍后我们分析如何发生的内存泄漏。

面板2:

Total Count:该类的实例个数

Heap Count:选定的Heap中实例的个数

Sizeof:每个实例占用的内存大小

Shallow Size:所有该类的实例占用的内存大小

Retained Size:该类的所有实例可支配的内存大小

面板3:

Instance:该类的所有实例对象(左侧Total Count为15,此处就有15个对象)

Depth:深度, GC Root点到该实例的最短链路数

Dominating Size:该实例可支配的内存大小

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此时发现面板下有个实例存在。

面板4:

Reference Tree:引用树

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过面板1我们发现有一个Activity发生了泄漏。我们可以通过Reference Tree面板就可以跟踪到该实例的引用树关系。

首先第一行我们发现一个LaunchActivity实例存在,然后展开该实例进一步查看该实例的引用关系,第二行我们可以看出它是被LaunchActity匿名内部类持有(this$0),这个匿名内部类实例是callBack,紧接着会发现该实例在mPermissionUtil实例中持有。

此时,我们可以进入代码查看该callBack是什么,然后在mPermissionUtils的持有。

LaunchActivity中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PermissionUitl中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

跟踪代码发现callback是一个接口,然后在LaunchActvity中调用了PermissionUtil的requestPermission(callback),然后将该callback赋值给PermissionUtil中成员引用,由于PermissionUtil是一个单例,然后new PermissionCallBack()匿名内部类会默认持有外部类引用,此时它将持有外部类LaunchActivity的实例,然后有赋值给了PermissionUtil中的成员引用,所以造成的内存泄漏。这种内存泄漏称之为一次性内存泄漏,只会发生一次且只会泄漏最后一次调用者。

通过使用Androd Studio自带的Dump Java Heap排查内存泄漏问题对于相对简单的泄漏场景比较适合,如果发生较为复杂的泄漏场景可能使用Dump Java Heap不太容易查找问题。此时我们可以借助另外一个工具:MAT (Memory Analyzer Tool)

4、MAT

Memory Analyzer Tool是Eclipse的一个插件,它的使用以及安装这部分资料非常多,故篇幅原因不在展开分析介绍。

下载地址:https://www.eclipse.org/mat/downloads.php

荐:https://blog.csdn.net/u010335298/article/details/52233689

荐:https://blog.csdn.net/itachi85/article/details/77075455

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5、其他

我们也可以借助第三方检测库,在运行期间检查内存泄漏情况:LeakCanary

LeakCanary是square出品的一个检测内存泄漏的库,集成到App之后便无需关心,在发生内存泄漏之后会Toast、通知栏弹出等方式提示,可以指出泄漏的引用路径,而且可以抓取当前的堆栈信息供详细分析。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分析内存泄漏主要是定位GC Root,只有明白GC Root点才能够准确分析定位内存泄漏问题。

三、内存抖动

内存抖动是指内存在短时间内频繁地分配和回收,而频繁的gc会导致卡顿,严重时和内存泄漏一样会导致OOM。

内存抖动为什么会造成OOM这关系到Java的垃圾回收。

1、常见内存抖动场景

循环中创建大量临时对象;

onDraw中创建Paint或Bitmap对象等;

2、内存抖动后果

瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。系统花费在GC上的时间越多,进行界面绘制或流音频处理的时间就越短**。**即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

四、onTrimMemory与onLowMemory

Android系统的每个进程都有一个最大内存限制,如果申请的内存资源超过这个限制,系统就会抛出OOM错误。

onTrimMemory

所以在实际开发过程中我们要尽可能避免内存泄漏与内存抖动之外,还要格外注意内存使用情况。根据《Manage Your App’s Memory》,我们可以对内存的状态进行监听,我们的Application、Acivity、Service、ContentProvider与Fragment都实现了ComponentCallbacks2接口。所以能够重写onTrimMemory与onLowMemory函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

onTrimMemory的参数是一个int数值,代表不同的内存状态:

TRIM_MEMORY_RUNNING_MODERATE:

你的应用正在运行并且不会被列为可杀死的。但是设备此时正运行于低内存状态下,系统开始触发杀死LRU Cache中的Process的机制。

TRIM_MEMORY_RUNNING_LOW:

你的应用正在运行且没有被列为可杀死的。但是设备正运行于更低内存的状态下,你应该释放不用的资源用来提升系统性能。

TRIM_MEMORY_RUNNING_CRITICAL:

你的应用仍在运行,但是系统已经把LRU Cache中的大多数进程都已经杀死,因此你应该立即释放所有非必须的资源。如果系统不能回收到足够的RAM数量,系统将会清除所有的LRU缓存中的进程,并且开始杀死那些之前被认为不应该杀死的进程,例如那个包含了一个运行态Service的进程。

当应用进程退到后台正在被Cached的时候,可能会接收到从onTrimMemory()中返回的下面的值之一:

TRIM_MEMORY_BACKGROUND:

系统正运行于低内存状态并且你的进程正处于LRU缓存名单中最不容易杀掉的位置。尽管你的应用进程并不是处于被杀掉的高危险状态,系统可能已经开始杀掉LRU缓存中的其他进程了。你应该释放那些容易恢复的资源,以便于你的进程可以保留下来,这样当用户回退到你的应用的时候才能够迅速恢复。

TRIM_MEMORY_MODERATE:

系统正运行于低内存状态并且你的进程已经已经接近LRU名单的中部位置。如果系统开始变得更加内存紧张,你的进程是有可能被杀死的。

TRIM_MEMORY_COMPLETE:

系统正运行于低内存的状态并且你的进程正处于LRU名单中最容易被杀掉的位置。你应该释放任何不影响你的应用恢复状态的资源。

TRIM_MEMORY_UI_HIDDEN:

UI不可见了,应该释放占用大量内存的UI数据。

比如说一个Bitmap,我们缓存下来是为了可能的(不一定)再次显示。但是如果接到这个回调,那么还是将它释放掉,如果回到前台,再显示会比较好。

onLowMemory

这个函数看名字就是低内存。这个函数的回调意味着后台进程已经被干掉了。这个回调可以作为4.0兼容onTrimMemory的TRIM_MEMORY_COMPLETE来使用

如果希望在其他组件中也能接收到这些回调可以使用上下文的registerComponentCallbacks注册接收,

unRegisterComponentCallbacks反注册

五、OutOfMemeory

OOM就是申请的内存超过了Heap的最大值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OOM的产生不一定是一次申请的内存就超过了最大值,导致oom的原因基本上都是一般情况,我们的不良代码平时”积累”下来的。

我们知道Android应用的进程都是从一个叫做Zygote的进程fork出来的。并且每个应Aandroid会对其进行内存限制。我们可以查看:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

六、有效减少内存占用的建议

1、使用Android优化过后的集合

在Android开发时,我们使用的大部分都是Java的api。其中我们经常会用到java中的集合,比如HashMap。

使用HashMap非常舒服,但是对于Android这种内存敏感的移动平台,很多时候使用这些Java的API并不能达到更好的性能,相反反而更消耗内存,所以针对Android,google也推出了更符合自己的API,比如SparseArray、ArrayMap用来代替HashMap在有些情况下能带来更好的性能提升。

注意:此处仅考虑内存占用情况,并且在一定的长度的数据集,并不是适合所有场景下。

2、集合初始长度

如:HashMap,他的默认长度为16,负载因子为0.75,如果我们知道要存放数据的长度如5,此时最合适的HashMap的初始容量为:5/0.75 = 7;

故:HashMap map = new HashMap(7)

3、Bitmap

Bitmap可以说是一个内存中的大胖子,作为现在Android开发程序是比较幸福,有很多关于图片加载优秀的库,如Glide。

有关于Bitmap的优化我们会在后续单独专题中介绍,故不在此处展开介绍。

荐:《Handling Bitmaps》

4、try{}cacth(Error){}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

[外链图片转存中…(img-EQ3MAkJx-1712021844835)]

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值