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

二、定位内存泄漏

1、初步定位是否发生内存泄漏

借助Android Studio的Monitor查看是否发生了内存泄漏情况

通过反复的执行同一个功能,触发GC操作,观察内存前后变化情况。

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

如果内存前后未发生明显变化(增加)此时可以初步判断未发生内存泄漏。

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

比如此时内存使用情况为:21.04MB,然后我们打开一个新的Activity,然后返回执行GC操作,观察此时的内存使用情况。

2、Monitor栏基本功能说明:

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

序号**1、**手动触发GC操作;

**序号2、**Dump Java Heap,获取当前的堆栈信息,生成一个.hprof文件,AndroidStudio会自动使用HeapViewer打开;一般用于操作之后检测内存泄漏的情况;

**序号3、**Start Allocation Tracking 内存分配追踪工具,用于追踪一段时间的内存分配使用情况,能够知道执行一些列操作后,有哪些对象被分配空间。一般用于追踪某项操作之后的内存分配,调整相关的方法调用来优化app性能与内存使用;

**序号4、**剩余可用 内存;

**序号5、**已经使用的内存;

3、Dump Java Heap进一步定位内存泄漏

通过Monitor栏只能初步粗略的观察是否发生内存泄漏,然而要真正的发现内存泄漏以及精确定位内存泄漏位置还需要借助相关工具分析排查。

点击Memory Monitor的Dump Java Heap,会生成一个.hprof文件,AndroidStudio会自动使用HeapViewer打开。

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

面板说明:

面板1:

Detect Leaked Activities :检测泄漏的Activity

Find Duplicate Strings :查找重复的字符串

默认两个选项都是勾选的。

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

点击绿色箭头,此时大家会看到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会对其进行内存限制。我们可以查看:

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

文末

架构师不是天生的,是在项目中磨练起来的,所以,我们学了技术就需要结合项目进行实战训练,那么在Android里面最常用的架构无外乎 MVC,MVP,MVVM,但是这些思想如果和模块化,层次化,组件化混和在一起,那就不是一件那么简单的事了,我们需要一个真正身经百战的架构师才能讲解透彻其中蕴含的深理。

移动架构师

系统学习技术大纲

一线互联网Android面试题总结含详解(初级到高级专题)

image

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
[外链图片转存中…(img-a3BLbiTH-1715351565109)]

一线互联网Android面试题总结含详解(初级到高级专题)

[外链图片转存中…(img-4mSrzyxr-1715351565110)]

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

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值