Profiler内存泄露实际案例分析

前言

本篇文章,将以一个实际案例为基础,从分析问题表象出发,在借助Profiler的情况下,教你如何一步一步剖析和解决问题。


案例

最近测试那边反馈了一个系统卡顿的问题
出现的场景,是在安卓设备(手机)运行一段时间后,会变得很卡顿。


分析一

系统出现卡顿,我们一般会从2个角度,切入分析:

  1. CPU占用率。
  2. 内存占用率。

  • CPU占用率分析

通过adb shell top命令,我们可以看到当前cpu占用率,以及每个进程的占用率,如下图所示:
Profiler内存泄露实际案例分析_第1张图片
这里面总的可用CPU可用率为800%,user进程占用100%,system进程占用291%空闲有391%。相当于,CPU空闲率几乎接近50%。由此,可以排除CPU占用率高的问题。

  • 内存占用率分析
    通过adb shell dumpsys meminfo命令,我们可以查看各个进程对内存的占用率,以及剩余可用的内存空间,如下图所示:
    Profiler内存泄露实际案例分析_第2张图片
    图1展示了各进程的内存占用率

Profiler内存泄露实际案例分析_第3张图片

图2展示了当前设备的内存占用率

这里的图1、图2,只是示例图,非当时案发现场的内存占用率。当时测试人员,测出系统卡顿时,中心服务的进程内存占用高达1.4+GBFree RAM,所剩无几。由此,可以初步判断,是中心服务进程内存占用过高,拖慢了整个系统,导致系统卡顿。

分析二

基于分析一的情况,接下来,我们借助Android Studio的Profiler工具对中心服务进程的内存占用,进行监控,如下图所示:
Profiler内存泄露实际案例分析_第4张图片
这里我放着煲机,煲了有十几分钟,通过图片,我们可以明显发现,随着时间的推移,Native层的内存占用率越来越高。

由此,我们可以进一步猜测,是底层代码没有释放相关的内存资源。此时,我们可以通过左边控制面板,通过选中Record nativ allocations,并点击Record按钮,进行内存分配的监控。监控时长,视内存飙升的速度而定,飙升的越高,监控时长可以越短。监控完,点击stop即可。

这里以60s为例,识别结果如下图所示:
Profiler内存泄露实际案例分析_第5张图片
通过图片的调用栈关系,我们可以看出是由中心服务进程中的语音模块,有个ZoneRecorder类,该类的runAudioRecorder方法,调用了jni层native-lib.cpp的代码。

最终触发,这个底层方法:JNIEnv::GetByteArrayElements(),这个方法在不断的吃内存。

随着监控时间变长,这个方法所占用的内存也会变大。

由此,我们已经找出了内存泄露的地方,通过注释掉ZoneRecorder.runAudioRecorder方法的实现,屏蔽掉JNI层的调用后,再重新运行中心服务,可以发现,native层的内存占用,会一直维持在一个较低且非常稳定的范围值之内。


总结,当出一个问题时,我们不能简单的惑于表面,就下判断。比如,出现系统卡顿时,不能第一时间就下定论说是系统的问题,然后就丢给系统人员。

应该抱持科学严谨的态度,对内存分配进行层层追踪,以事实为依据,下的定论,才能更让人信服。

你可能感兴趣的:(Android,进阶知识,android)