欢迎光临
专注android技术,聚焦行业精粹,我们一直在努力

Android-内存分析OOM

1、内存基础知识

1.1、堆和栈

1.1.1、栈内存的基础知识

栈内存用于存储java的数据类型,方法调用,对象的引用。它包含了在堆中存储的对象的短生命周期的(弱)引用。当一个方法被调用的时候,栈中会预留出一块内存给该方法,它的基本类型数据和方法作用域范围内的所有的对象的引用。当方法执行结束的时候,方法块占用的内存将被回收,留给其它方法使用。当栈里面所有的空间都被占满的时候,jvm将会抛出java.lang.StackOverFlowError 异常。

如下图所示:

1.1.2、堆内存基础知识

堆内存在java中用于存储对象和java 类。当我们创建一个对象时它会在堆中分配内存。任何一个在堆中创建的对象可以被所有线程访问,它有全局的访问权限。在应用里面任何时候我们可以引用那个对象。

在一定的时候垃圾回收器会运行并且回收堆里面没有被引用的对象所占用的空间。当堆里面所有的空间都被占满的时候jvm将会抛出java.lang.OutOfMemoryError异常。

通过使用Android的内存分析工具和profiler,我们可以知道对象占用了多少的内存空间,也可以知道堆里面剩余/消耗了多少的内存空间。

下图展示了堆内存的使用:

1.1.3、一个完整的例子看堆和栈的如何被占用

堆和栈的占用情况如下图所示:

1.1.4、栈和堆的不同

基础数据类型和对象的引用保存在栈里面

每一个对象都在堆里面分配空间

栈内存比堆内存空间小

栈内存包含程序的局部变量,堆里面包含了程序里面可以全局访问的对象

栈内存中当方法执行完后局部变量将会被删除并释放空间给其它的方法和变量。而堆内存则不同,只要有任何一个的引用指向某一个对象,这个对象将持续保留在堆内存中。当某个对象没有任何一个引用指向它,这个对象可以随时被垃圾回收机制删除并释放堆内存空间给其它的对象。

 

1.2、内存耗用:VSS/RSS/PSS/USS 的介绍

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS

1.2.1、VSS – Virtual Set Size (用处不大)

虚拟耗用内存(包含共享库占用的全部内存,以及分配但未使用内存)。其大小还包括了可能不在RAM中的内存(比如虽然malloc分配了空间,但尚未写入)。VSS 很少被用于判断一个进程的真实内存使用量。

VSS

1.2.2、RSS – Resident Set Size (用处不大)

实际使用物理内存(包含共享库占用的全部内存)。但是RSS还是可能会造成误导,因为它仅仅表示该进程所使用的所有共享库的大小,它不管有多少个进程使用该共享库,该共享库仅被加载到内存一次。所以RSS并不能准确反映单进程的内存占用情况

RSS

1.2.3、PSS – Proportional Set Size (仅供参考)

实际使用的物理内存(比例分配共享库占用的内存,按照进程数等比例划分)。例如:如果有三个进程都使用了一个共享库,共占用了30页内存。那么PSS将认为每个进程分别占用该共享库10页的大小。 PSS是非常有用的数据,因为系统中所有进程的PSS都相加的话,就刚好反映了系统中的 总共占用的内存。 而当一个进程被销毁之后, 其占用的共享库那部分比例的PSS,将会再次按比例分配给余下使用该库的进程。这样PSS可能会造成一点的误导,因为当一个进程被销毁后, PSS不能准确地表示返回给全局系统的内存

PSS

1.2.4、USS – Unique Set Size (非常有用)

进程独自占用的物理内存(不包含共享库占用的内存)。USS是非常非常有用的数据,因为它反映了运行一个特定进程真实的边际成本(增量成本)。当一个进程被销毁后,USS是真实返回给系统的内存。当进程中存在一个可疑的内存泄露时,USS是最佳观察数据。

USS

1.2.5、查看应用程序占用内存情况

从上面我们可以知道PSS和USS对我们分析内存问题帮助很大,我们可以借助android提供的dumpsys procstats命令来查看一个应用的内存使用情况:

输出中PSS和USS是按照如下的格式展示的:minPSS-avgPSS-maxPSS/minUSS-avgUSS-maxUSS

例如我们查看手机phone程序的内存使用情况:

1.2.6、查看系统当前的内存使用情况

Linux查看内存使用情况,可以查看/proc/meminfo和使用free命令

查看/proc/meminfo 会输出以下信息:

各字段含义如下:

1、MemTotal:内存总数

系统从加电开始到引导完成,BIOS等要保留一些内存,内核要保留一些内存,最后剩下可供系统支配的内存就是MemTotal。这个值在系统运行期间一般是固定不变的。

2、MemFree:空闲内存数

表示系统尚未使用的内存。MemUsed=MemTotal-MemFree就是已被用掉的内存。

3、MemAvailable:可用内存数

应用程序可用内存数。系统中有些内存虽然已被使用但是可以回收的,比如cache/buffer、slab都有一部分可以回收,所以MemFree不能代表全部可用的内存,这部分可回收的内存加上MemFree才是系统可用的内存,即:MemAvailable≈MemFree+Buffers+Cached,它是内核使用特定的算法计算出来的,是一个估计值。它与MemFree的关键区别点在于,MemFree是说的系统层面,MemAvailable是说的应用程序层面。

4、Buffer:缓冲区内存数

5、Cache:缓存区内存数

6、Shared:多个进程共享的内存空间,不常用,暂不讨论。

  Buffer与Cache的区别:

这里说下buffer与cache的区别,首先,从字面意义上讲,buffer是缓冲的意思,cache是缓存的意思。举个现实中的例子,比说铁道头上像弹簧一样的东西,就叫缓冲;部署在森林里的存应急物资的保管箱,名叫“Food Cache”,类似一种保存箱。  其次,常见的说法,叫write-buffer和read-cache,buffer一般用作写操作上,cache一般用在读操作上,不过也不是一成不变的;  举例说明,每秒要写100次硬盘,对系统冲击很大,浪费了大量时间在忙着处理开始写和结束写这两件事嘛。用buffer暂存起来,变成每10秒写一次硬盘,对系统的冲击就很小,写入效率高了;Cache 是为了弥补高速设备和低速设备的鸿沟而引入的中间层,最终起到加快取速度的作用。比如你一个很复杂的计算做完了,下次还要用结果,就把结果放手边一个好拿的地方存着,下次不用再算了,加快了数据取用的速度。(可以参考知乎上的回答:https://www.zhihu.com/question/26190832

计算公式:

参照free的输出结果,这里有几个计算公式:

为了直观说明,把第2行,Mem的行我这里称作OS Mem,第三行 buffers/cache行,称作APP buffer/cache:

OS Mem total = OS Mem used + OS Mem free

APP buffers/cache used = OS Mem used – OS Mem buffers – OS Mem cached

APP buffers/cache free = OS Mem free + OS Mem buffers + OS Mem cached

APP buffers/cache total = APP buffers/cache used + APP buffers/cache free = OS Mem total

 

2、OOM

2.1、堆内存溢出

什么导致了OOM的产生?

下面是几个关于Android官方声明内存限制阈值的API:

通常认为OOM发生是由于java堆内存不够用了,即Runtime.getRuntime().maxMemory()这个指标满足不了申请堆内存大小时,通常这种OOM的错误信息通常如下:

 

2.2、栈内存溢出

而发生 OOM 不一定是因为设备的内存(堆内存)不够用,也就是说内存宽裕也可能发生 OOM,比如栈内存溢出。例如我们会遇到这样的栈内存溢出的崩溃:

这个崩溃是由于linux系统对进程的限制导致的。我们可以通过查看以下几个文件来了解linux系统对对应进程的限制:

2.2.1、/proc/pid/limits

下面是一个样例:

  • Max stack size: Max processes的限制是整个系统的,不是针对某个进程的
  • Max locked memory: 线程创建过程中分配线程私有stack使用的mmap调用没有设置MAP_LOCKED,所以这个限制与线程创建过程无关
  • Max pending signals: c层信号个数阈值
  • Max msgqueue size: Android IPC机制不支持消息队列

2.2.2、/proc/sys/kernel

/proc/sys/kernel是一个目录里面包含了很多文件,如下图所示:

不同的手机厂商会对单个进程的创建的线程数限制不同,默认单个线程可创建的线程数挺大的,一版不会触发栈内存溢出。如下图所示,我们打开模拟器的threads-max文件可以看到单个进程可以同时存在的最大线程数是19587,这个数字挺大,一般的应用不容易触发这个上限:

 

2.2.3、由于系统限制导致的栈内存OOM崩溃例子

2.2.3.1、max open files

当进程fd数(可以通过 ls /proc/pid/fd | wc -l 获得)突破 /proc/pid/limits中规定的Max open files时,产生OOM。错误信息及堆栈如下:

2.2.3.2、threads-max

当线程数(可以在/proc/pid/status中的threads项实时查看)超过/proc/sys/kernel/threads-max中规定的上限时产生OOM崩溃。我们也可以通过这个命令实时查询某个进程id对应的线程数:

在Android7.0及以上的华为手机(EmotionUI_5.0及以上)的手机产生OOM,这些手机的线程数限制都很小(应该是华为rom特意修改的limits),每个进程只允许最大同时开500个线程,因此很容易复现了。OOM时错误信息如下:

2.2.3.4、进程的逻辑地址空间不够的时候也会产生OOM

首先,啥是 locked memory?
有些程序不希望自己申请的内存被 swap 到硬盘中,所以在申请内存时,会加上 MAP_LOCKED 这个 flag,这样就会得到 locked memory.
memlock 就是限制进程所能申请的 locked memory 大小。

逻辑地址空间不够(已用逻辑空间地址可以查看/proc/pid/status中的VmPeak/VmSize记录),此时创建线程产生的OOM具有如下信息:

 

3、Android dumpsys 工具

dumpsys 是一个可以在Android设备上运行和提供关于系统服务信息的工具。你可以在命令行里面使用adb调用dumpsys来获取已经连接USB设备的所有系统服务的诊断信息。这个输出的信息会比较多,我们可以在命令行中加一些可选的参数来过滤你感兴趣的信息。

我们常用的服务有:activity、package、cpuinfo、meminfo、procstats

command 描述
adb shell dumpsys activity 获取当前 Android 系统 Activity 栈中 Activity 信息
adb shell dumpsys activity top 获取当前 Android 系统 中与用户交互的 Activity 的详细信息
adb shell dumpsys meminfo [应用包名] 查看应用的内存使用情况
adb shell dumpsys package [应用包名] 获取手机里面某个 apk 的应用信息、版本信息
adb shell dumpsys activity activities 显示当前所有在运行的任务栈,并可查看栈中所有的 Activity 的列表
adb shell dumpsys usagestats 每个界面启动的时间
adb shell dumpsys procstats 应用内存的使用情况

3.1、使用procstats查看内存使用

我们可以使用adb shell dumpsys procstats --hours 3来收集最近3小时内存信息。输出信息显示了所有在过去3小时里面运行过的进程,排序是按照最早运行的排在第一个位置(进程如果是暂存状态将不会计算在整体的时间里,所以不影响排序)。通过这个命令的输出我们可以清晰的看到有一大部分的进程一直在运行,有的只是偶尔运行——例如Magazines进程,它在过去3小时的运行时间占用了仅仅3.6%的时间。

百分比告诉了我们每个进程在各种关键状态下花费的时间。内存的数字信息告诉了我们在这些关键状态的内存样本信息,例如minPss-avgPss-maxPss / minUss-avgUss-maxUss.

我们看到输出信息中只包含一部分状态:Imp Fg, Imp Bg, Service, Serivce Rs和Receiver。这些都是进程在后台一直活跃的场景,

4、Android studio profiler

Android Studio 3.0 采用全新的 **Android Profiler ** 窗口取代 Android Monitor 工具。 这些全新的分析工具能够提供关于应用 CPU、内存和网络 Activity 的实时数据。 您可以执行基于样本的函数跟踪来记录代码执行时间、采集堆转储数据、查看内存分配,以及查看网络传输文件的详情。

要打开 Android Profiler 窗口,请按以下步骤操作:

  1. 点击 View > Tool Windows > Android Profiler(也可以点击工具栏中的 Android Profiler )。
  2. 在 Android Profiler 窗口顶部(如图 1 所示),选择您想要分析的设备 1 和应用进程 2。如果您通过 USB 连接了某个设备但该设备未在设备列表中列出,请确保您已启用 USB 调试。如果您使用的是 Android Emulator 或已取得 root 权限的设备,Android Profiler 将列出所有正在运行的进程,即使这些进程可能无法调试。 当您发布可调试应用时,将会默认选择此进程。

Android Profiler 目前可显示共享时间线视图(图 1),其中包括带有 CPU、内存和网络使用信息实时图表的时间线。

4.1、使用 CPU Profiler 检查 CPU Activity 和函数跟踪

CPU Profiler 可帮助您实时检查应用的 CPU 使用率和线程 Activity,并记录函数跟踪,以便您可以优化和调试您的应用代码。

要打开 CPU Profiler,请按以下步骤操作:

  1. 点击 View > Tool Windows > Android Profiler(也可以点击工具栏中的 Android Profiler )。
  2. 从 Android Profiler 工具栏中选择您想要分析的设备和应用进程。 如果您通过 USB 连接了某个设备但该设备未在设备列表中列出,请确保您已启用 USB 调试
  3. 点击 CPU 时间线中的任意位置即可打开 CPU Profiler。

通过这个工具我们可以观察和分析上面提到的线程数超出限制导致的栈内存溢出问题。

4.1.2、为什么要分析 CPU 使用率

最大限度减少应用的 CPU 使用率具有许多优势,如提供更快更顺畅的用户体验,以及延长设备电池续航时间。 它还可帮助应用在各种新旧设备上保持良好性能。 与应用交互时,您可以使用 CPU Profiler 监控 CPU 使用率和线程 Activity。 不过,如需了解应用如何执行其代码的详细信息,您应记录和检查函数跟踪

对于应用进程中的每个线程,您可以查看一段时间内执行了哪些函数,以及在其执行期间每个函数消耗的 CPU 资源。 您还可以使用函数跟踪来识别调用方被调用方。 调用方指调用其他函数的函数,而被调用方是指被其他函数调用的函数。 您可以使用此信息确定哪些函数负责调用常常会消耗大量特定资源的任务,并尝试优化应用代码以避免不必要的工作。

如果您想收集可帮助您检查原生系统进程的详细系统级数据,并解决掉帧引起的界面卡顿,您应使用 systrace

或者,如果您想导出您使用 Debug 类捕获的 .trace 文件,您应使用 Traceview

4.1.3、CPU Profiler 概览

当您打开 CPU Profiler 时,它将立即开始显示应用的 CPU 使用率和线程 Activity。 您应该会看到类似图 1 的一些内容:

图 1. CPU Profiler

如图 1 所示,CPU Profiler 的默认视图包括以下内容:

  1. Event 时间线: 显示应用中在其生命周期不同状态间转换的 Activity,并表明用户与设备的交互,包括屏幕旋转 Event。 如需了解有关 Event 时间线的更多信息,包括如何启用它,请阅读 启用高级分析
  2. CPU 时间线: 显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用使用的总线程数。 此时间线还显示其他进程的 CPU 使用率(如系统进程或其他应用),以便您可以将其与您的应用使用率进行对比。 通过沿时间线的水平轴移动鼠标,您还可以检查历史 CPU 使用率数据。
  3. 线程 Activity 时间线: 列出属于应用进程的每个线程并使用下面列出的颜色沿时间线标示它们的 Activity。 在您记录一个函数跟踪后,您可以从此时间线中选择一个线程以在跟踪窗格中检查其数据。
    • 绿色: 表示线程处于活动状态或准备使用 CPU。 即,它正在“运行中”或处于“可运行”状态。
    • 黄色: 表示线程处于活动状态,但它正在等待一个 I/O 操作(如磁盘或网络 I/O),然后才能完成它的工作。
    • 灰色: 表示线程正在休眠且没有消耗任何 CPU 时间。 当线程需要访问尚不可用的资源时偶尔会发生这种情况。 线程进入自主休眠或内核将此线程置于休眠状态,直到所需的资源可用。
  4. 记录配置: 允许您选择以下选项之一以确定分析器记录函数跟踪的方式。
    • Sampled: 一个默认配置,在应用执行期间频繁捕获应用的调用堆栈。 分析器比较捕获的数据集以推导与应用代码执行有关的时间和资源使用信息。 基于“Sampled”的跟踪的固有问题是,如果应用在捕获调用堆栈后进入一个函数并在下一次捕获前退出该函数,则分析器不会记录该函数调用。 如果您对此类生命周期很短的跟踪函数感兴趣,您应使用“Instrumented”跟踪。
    • Instrumented: 一个默认配置,在运行时设置应用以在每个函数调用的开始和结束时记录时间戳。 它收集时间戳并进行比较,以生成函数跟踪数据,包括时间信息和 CPU 使用率。 请注意,与设置每个函数关联的开销会影响运行时性能,并可能会影响分析数据,对于生命周期相对较短的函数,这一点更为明显。 此外,如果应用短时间内执行大量函数,则分析器可能会迅速超出它的文件大小限制,且不能再记录更多跟踪数据。
    • Edit configurations: 允许您更改上述“Sampled”和“Instrumented”记录配置的某些默认值,并将它们另存为自定义配置。 如需了解更多信息,请转到创建记录配置部分。
  5. 记录按钮: 用于开始和停止记录函数跟踪。 如需了解更多信息,请转到记录和检查函数跟踪部分 。

注: 分析器还会报告 Android Studio 和 Android 平台添加到您的应用进程(如 JDWPProfile SaverStudio:VMStatsStudio:Perfa 以及 Studio:Heartbeat,尽管它们在线程 Activity 时间线中显示的确切名称可能有所不同)的线程 CPU 使用率。 这表示 CPU 时间线中应用的 CPU 使用率还可反映这些线程使用的 CPU 时间。 您可以在线程 Activity 时间线中查看其中的一些线程并监控其 Activity。 (不过,由于分析器线程执行原生代码,因此,您无法为它们记录函数跟踪数据。)Android Studio 将报告此数据,以便当线程 Activity 及 CPU 使用率实际上是由应用代码引发时,您可以轻松识别。

4.1.4、记录和检查函数跟踪

要开始记录函数跟踪,从下拉菜单中选择 Sampled 或 Instrumented 记录配置,或选择您创建的自定义记录配置,然后点击 Record 。与应用交互并在完成后点击 Stop recording。分析器将自动选择记录的时间范围,并在函数跟踪窗格中显示其跟踪信息,如图 2 所示。如果您想检查另一个线程的函数跟踪,只需从线程 Activity 时间线中选中它。

图 2. 记录函数跟踪后的 CPU Profiler

  1. 选择时间范围: 用于确定您要在跟踪窗格中检查所记录时间范围的哪一部分。 当您首次记录函数跟踪时,CPU Profiler 将在 CPU 时间线中自动选择您的记录的完整长度。 如果您想仅检查所记录时间范围一小部分的函数跟踪数据,您可以点击并拖动突出显示的区域边缘以修改其长度。
  2. 时间戳: 用于表示所记录函数跟踪的开始和结束时间(相对于分析器从设备开始收集 CPU 使用率信息的时间)。 在选择时间范围时,您可以点击时间戳以自动选择完整记录,如果您有多个要进行切换的记录,则此做法尤其有用。
  3. 跟踪窗格: 用于显示您所选的时间范围和线程的函数跟踪数据。 仅在您至少记录一个函数跟踪后此窗格才会显示。 在此窗格中,您可以选择想如何查看每个堆叠追踪(使用跟踪标签),以及如何测量执行时间(使用时间引用下拉菜单)。
  4. 选择后,可通过 Top Down 树、Bottom Up 树、调用图表火焰图的形式显示您的函数跟踪。 您可以在下文中了解每个跟踪窗格标签的更多信息。
  5. 从下拉菜单中选择以下选项之一,以确定如何测量每个函数调用的时间信息:
    • Wall clock time:壁钟时间信息表示实际经过的时间。
    • Thread time:线程时间信息表示实际经过的时间减去线程没有消耗 CPU 资源的任意时间部分。 对于任何给定函数,其线程时间始终少于或等于其壁钟时间。 使用线程时间可以让您更好地了解线程的实际 CPU 使用率中有多少是给定函数消耗的。

4.1.5、使用 Call Chart 标签检查跟踪

Call Chart 标签提供函数跟踪的图形表示形式,其中,水平轴表示函数调用(或调用方)的时间段和时间,并沿垂直轴显示其被调用者。 对系统 API 的函数调用显示为橙色,对应用自有函数的调用显示为绿色,对第三方 API(包括 Java 语言 API)的函数调用显示为蓝色。 下面的图 3 展示了一个调用图表示例,并描绘了给定函数的 self time、children time 以及总时间的概念。 您可以在如何使用 Top Down 和 Bottom Up 检查跟踪部分详细了解这些概念。

图 3. 一个调用图表示例,描绘了函数 D 的 self、children 及总时间

提示: 若要跳转到某个函数的源代码,请右键点击该函数并选择 Jump to Source。 这适用于任一跟踪窗格标签。

4.1.6、使用 Flame Chart 标签检查跟踪

Flame Chart 标签提供一个倒置的调用图表,其汇总相同的调用堆栈。 即,收集共享相同调用方顺序的完全相同的函数,并在火焰图中用一个较长的横条表示它们(而不是将它们显示为多个较短的横条,如调用图表中所示)。 这样更方便您查看哪些函数消耗最多时间。 不过,这也意味着水平轴不再代表时间线,相反,它表示每个函数相对的执行时间。

为帮助说明此概念,请考虑以下图 4 中的调用图表。 请注意,函数 D 多次调用 B(B1、B2 和 B3),其中一些对 B 的调用也调用了 C(C1 和 C3)。

图 4. 包含多个共享通用调用方顺序的函数调用的调用图表

由于 B1、B2 和 B3 共享相同的调用方顺序 (A → D → B),因此,可将它们汇总在一起,如下所示。 同样,将 C1 和 C3 汇总在一起,因为它们也共享相同的调用方顺序 (A → D → B → C)—请注意,未包含 C2,因为它具有不同的调用方顺序 (A → D → C)。

图 5. 汇总共享相同调用堆栈的相同函数

汇总的函数调用用于创建火焰图,如图 6 所示。请注意,对于火焰图中任何给定的函数调用,消耗最多 CPU 时间的被调用方首先显示。

图 6. 图 4 中显示的调用图表的火焰图表示形式

4.2、使用 Memory Profiler 查看 Java 堆和内存分配

Memory Profiler 是 Android Profiler 中的一个组件,可帮助您识别导致应用卡顿、冻结甚至崩溃的内存泄漏和流失。 它显示一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。

要打开 Memory Profiler,请按以下步骤操作:

  1. 点击 View > Tool Windows > Android Profiler(也可以点击工具栏中的 Android Profiler )。
  2. 从 Android Profiler 工具栏中选择您想要分析的设备和应用进程。 如果您通过 USB 连接了某个设备但该设备未在设备列表中列出,请确保您已启用 USB 调试
  3. 点击 **MEMORY **时间线中的任意位置可打开 Memory Profiler。

或者,您可以在命令行中使用 dumpsys 检查您的应用内存,同时查看 logcat 中的 GC Eve

4.2.1、为什么应分析您的应用内存

Android 提供一个托管内存环境—当它确定您的应用不再使用某些对象时,垃圾回收器会将未使用的内存释放回堆中。 虽然 Android 查找未使用内存的方式在不断改进,但对于所有 Android 版本,系统都必须在某个时间点短暂地暂停您的代码。 大多数情况下,这些暂停难以察觉。 不过,如果您的应用分配内存的速度比系统回收内存的速度快,则当收集器释放足够的内存以满足您的分配需要时,您的应用可能会延迟。 此延迟可能会导致您的应用跳帧,并使系统明显变慢。

尽管您的应用不会表现出变慢,但如果存在内存泄漏,则即使应用在后台运行也会保留该内存。 此行为会强制执行不必要的垃圾回收 Event,因而拖慢系统的内存性能。 最后,系统被迫终止您的应用进程以回收内存。 然后,当用户返回您的应用时,它必须完全重启。

为帮助防止这些问题,您应使用 Memory Profiler 执行以下操作:

  • 在时间线中查找可能会导致性能问题的不理想的内存分配模式。
  • 转储 Java 堆以查看在任何给定时间哪些对象耗尽了使用内存。 长时间进行多个堆转储可帮助识别内存泄漏。
  • 记录正常用户交互和极端用户交互期间的内存分配以准确识别您的代码在何处短时间分配了过多对象,或分配了泄漏的对象。

如需了解可减少应用内存使用的编程做法,请阅读管理您的应用内存

更详细的如何使用profiler分析内存信息参考官方文章:链接

4.3、利用 Network Profiler 检查网络流量

Network Profiler 能够在时间线上显示实时网络 Activity,包括发送和接收的数据以及当前的连接数。 这便于您查看应用传输数据的方式和时间,并据此对底层代码进行适当优化。

要打开 Network Profiler,请按以下步骤操作:

  1. 点击 View > Tool Windows > Android Profiler(也可以点击工具栏中的 Android Profiler )。
  2. 从 Android Profiler 工具栏中选择您想要分析的设备和应用进程。 如果您通过 USB 连接了某个设备但该设备未在设备列表中列出,请确保您已启用 USB 调试
  3. 点击 **NETWORK **时间线中的任意位置即可打开 Network Profiler。

4.3.1、为什么应分析应用的网络 Activity

当您的应用向网络发出请求时,设备必须使用高功耗的移动或 WLAN 无线装置来收发数据包。 无线装置不仅要消耗电力来传输数据,还需要消耗额外的电力来开启并且不锁定屏幕。

使用 Network Profiler,您可以查找频繁出现的短时网络 Activity 峰值,这意味着您的应用需要经常打开无线装置,或需要长时间不锁定屏幕以处理集中出现的大量短时请求。 这种模式说明您可以通过批量处理网络请求,减少必须开启无线装置来发送或接收数据的次数,从而优化应用,改善电池续航表现。 这种方式还能让无线装置调整到低能耗模式,延长批量处理请求之间的间隔时间,节省能耗。

要详细了解优化应用网络 Activity 的相关技巧,请参阅减少网络耗电量

4.3.2、Network Profiler 概览

窗口顶部显示的是 Event 时间线以及 1 无线装置功耗状态(低/高)与 WLAN 的对比。 在时间线上,您可以 2 点击并拖动选择时间线的一部分来检查网络流量。 下方的 3 窗口会显示在时间线的选定片段内收发的文件,包括文件名称、大小、类型、状态和时间。 您可以点击任意列标题为此列表排序。 同时,您还可以查看时间线选定片段的明细数据,显示每个文件的发送或接收时间。

点击网络连接的名称即可查看 4 有关所发送或接收的选定文件的详细信息。 点击各个标签可查看响应数据、标题信息或调用堆栈。

图 1. Network Profiler 窗口

注: 必须启用高级分析才能从时间线中选择要检查的片段,查看发送和接收的文件列表,或查看有关所发送或接收的选定文件的详细信息。 要启用高级分析,请参阅启用高级分析

4.3.3、排查网络连接问题

如果 Network Profiler 检测到流量值,但无法识别任何受支持的网络请求,您会收到以下错误消息:

Network Profiling Data Unavailable: There is no information for the network traffic you’ve selected.”

Network Profiler 目前只支持 HttpURLConnection 和 OkHttp 网络连接库。 如果您的应用使用的是其他网络连接库,则可能无法在 Network Profiler 中查看网络 Activity。

4.3.4、启用高级分析

要显示高级分析数据,Android Studio 必须在您编译后的应用中插入监控逻辑。 高级分析工具提供的功能包括:

  • Event 时间线(所有分析器窗口中均有)
  • 分配对象数量(Memory Profiler 中)
  • 垃圾回收 Event(Memory Profiler 中)
  • 有关所有传输的文件的详情(Network Profiler 中)

要启用高级分析,请按以下步骤操作:

  1. 选择 Run > Edit Configurations
  2. 在左侧窗格中选择您的应用模块。
  3. 点击 Profiling 标签,然后勾选 Enable advanced profiling

现在重新构建并运行您的应用,即可获取完整的分析功能。 但请注意,高级分析会减缓您的构建速度,所以仅当您想要开始分析应用时才启用此功能。

注:对于原生代码,不可使用高级分析功能。 如果您的应用是纯原生应用(不含 Java Activity 类),则不可使用高级分析功能。 如果您的应用使用了 JNI,则可使用部分高级分析功能,例如 Event 时间线、GC Event、Java 分配对象和基于 Java 的网络 Activity,但不能检测基于原生的分配和网络 Activity。

赞(1) 打赏
未经允许不得转载:花花鞋 » Android-内存分析OOM
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

国内精品Android技术社区

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏