FOOM/OOM

最近更新时间:2025-11-04 16:47:11

我的收藏

FOOM(iOS)

FOOM(Foreground Out Of Memory),前台内存溢出,我们将 APP 在前台触发到系统内存限制而被 SIGKILL 信号杀死的情况,定义为 FOOM,本文介绍了 有关 FOOM 上报的各种指标分析以及如何查看详细问题上报。

指标分析

趋势分析

趋势图会以折线等形式展示不同类型 FOOM 率(如设备 FOOM 率、人数 FOOM 率等)随时间的变化趋势,单位为(%)。可通过展示选项调整展示形式,也可单击加入对比列表将不同数据系列加入对比。

对比列表区域可查看不同筛选条件下的 FOOM 相关数据明细,包括序号、时间范围、APP 版本等信息,还可进行删除、批量删除、趋势预览等操作。
勾选一条或多条需删除的数据后,然后单击删除批量删除,即可完成删除。

单击趋势预览即可查看对比列表中已选择的 FOOM 率总览。


多维分析

筛选条件设置:可设置时间范围(默认或自定义)、APP 版本、系统版本、SDK 版本、场景、上报量、机型、厂商等筛选条件,还可选择是否过滤 DAU 占比小于1%的 APP 版本。设置完成后,单击查询将获取筛选后数据。若要收起筛选条件,可单击收起筛选项
数据展示:该区域会展示设备 FOOM 率、人数 FOOM 率、次数 FOOM 率、单位时间 FOOM 率、上报量、占比、累计占比等多维度的 FOOM 相关数据,帮助用户从不同角度分析 FOOM 指标情况。


问题列表

FOOM 的问题列表和崩溃监控模块类似,详情请参见 崩溃问题列表

个例聚类

平台尽量将上报的个例进行聚类,生成 issue,方便业务开发以 issue 维度跟进定位问题。目前平台通过 FOOM 个例中的分配堆栈提取信息聚类,因为 FOOM 采集堆栈的策略问题,并非所有的上报个例都拥有分配堆栈信息,因此对于无堆栈的问题,会进入一个特殊的 issue 类别:"无堆栈问题" 。其他包含堆栈的个例,则提取堆栈中的特征,形成聚类特征进行聚类。

注意:
由于开启堆栈的占比较少,因此绝大部分的个例问题,都会被聚类在无堆栈问题中。

问题详情

个例详情

FOOM > 问题列表进入到一个 issue 中,会展示该 issue 下的所有个例问题。个例问题除基本的信息外,最重要的部分便是堆栈分配详情数据。


内存分配堆栈

堆栈分配详情
堆栈分配详情中的数据便是 SDK 收集的 APP 在运行过程中记录到的 malloc logger 信息。
在 malloc logger 记录中,按照相同的分配类型和分配堆栈进行了合并,展示对应的堆栈分配的数量和总大小。

注意:
此处堆栈不会包含所有的内存分配,只会包含在现有策略下记录到分配而未释放的内存操作行为,未在记录策略中的分配不会包含。在默认记录策略下,只有同时满足以下条件的分配才会记录:
单次分配大于阈值,默认阈值为8K,可通过配置调整。
相同堆栈累计分配大于阈值,默认阈值为512K,可通过配置调整。
堆栈分配记录开启前的分配不会包含:在记录逻辑开始前已分配的内存不会在记录中。
堆栈分配树、火焰图
堆栈分配树和火焰图是对堆栈分配详情中的堆栈数据进行聚合产生的数据,其没有特殊含义,只是通过聚合为树结构后,可以更直观的发现内存分配的占比等情况。
堆栈分配树

火焰图


VMMAP

VMMAP 指的是虚拟内存管理器(VMM)的内存分配表,记录了所有内存区域的分配信息,包括堆栈内存。

在上述记录策略外的内存问题,堆栈分配详情可能无法直接提供有效信息,因此后续引入了 VMMAP 数据。VMMAP 数据类似 vmmap 命令的输出,提供的是内核管理的内存信息的使用情况,包括 user_tag、virtual_size、dirty_size 等信息。
user_tag:与内存堆栈分配详情中的 "分配类型" 对应。
virtual_size:表示该类型使用逻辑地址大小,不代表实际的物理占用内存。
dirty_size:表示该类型使用的无法被操作系统回收使用的内存,占用实际的物理内存。
SDK 会周期性采集该数据,其采集频率与内存指标相关,因此其拿到的值为峰值内存时的 VMMAP 信息,基本包含所有的内存使用情况。因采集频率的缘故,可能无法与最终的峰值内存一致。
通过 VMMAP 信息,可以大致了解到主要的内存使用情况,再结合其他因素可进行进一步分析。

内存分配列表

FOOM 内存分配列表功能主要用于查询和管理与内存分配相关的问题,帮助开发者或运维人员快速定位、分析内存分配方面的异常情况,如内存分配过大、影响设备数多等问题,以便及时处理,保障应用的稳定运行。

查询

支持用户通过多个筛选项进行内存分配的查询,详情请参见 查询


FOOM 分配列表

用户查询后,结果会在 FOOM 分配列表中展示,包括问题特征、最近上报时间、影响设备数、上报次数、内存分配总大小等信息。

同时,可进行以下操作:
问题标签分布:查看不同问题标签的分布情况,便于统计和分析问题类型。

批量操作:对符合条件的多条数据进行批量处理,提高工作效率。

设置:可对列表的展示等进行相关设置,以满足个性化需求。


OOM(Android)

背景

在 Android 开发中,OOM(Out of Memory)问题通常指的是 Java 堆内存不足导致的 java.lang.OutOfMemoryError 异常。然而,OOM 问题不仅限于 Java 堆内存,还可能涉及到文件描述符(FD)资源使用超标或本地内存地址空间使用超标,例如下面这个很常见的 java.lang.OutOfMemoryError 异常:
java.lang.OutOfMemoryError: Could not allocate JNI Env
java.lang.Thread.nativeCreate(Native Method)
java.lang.Thread.start(Thread.java:1063)
kotlinx.coroutines.scheduling.CoroutineScheduler.int createNewWorker()(CoroutineScheduler.java:485)
kotlinx.coroutines.scheduling.CoroutineScheduler.boolean tryCreateWorker(long)(CoroutineScheduler.java:440)
kotlinx.coroutines.scheduling.CoroutineScheduler.boolean tryCreateWorker$default(kotlinx.coroutines.scheduling.CoroutineScheduler,long,int,java.lang.Object)(CoroutineScheduler.java:431)
kotlinx.coroutines.scheduling.CoroutineScheduler.void signalCpuWork()(CoroutineScheduler.java:427)
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.void beforeTask(int)(CoroutineScheduler.java:758)
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.void executeTask(kotlinx.coroutines.scheduling.Task)(CoroutineScheduler.java:749)
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.void runWorker()(CoroutineScheduler.java:678)
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.void run()(CoroutineScheduler.java:665)
表面上看是一个虚拟机内存相关的问题,但从源码角度来看,其实是因为创建线程的时候没有可用的 FD 资源,创建线程失败 ,所以上报为 java.lang.OutOfMemoryError 异常,从系统日志来看也能验证这一点,在出错之前,有很多 “Too many open files” 的系统日志 :
03-18 13:44:17.426 28516 809 W System.err: java.net.ConnectException: failed to connect to up-hl.3g.qq.com/61.241.53.46 (port 443) after 10000ms: connect failed: EMFILE (Too many open files)
03-18 13:44:17.427 28516 809 W System.err: Caused by: android.system.ErrnoException: connect failed: EMFILE (Too many open files)
03-18 13:44:17.519 28516 765 W System.err: java.io.FileNotFoundException: /data/user/0/******.temp: open failed: EMFILE (Too many open files)
03-18 13:44:17.519 28516 765 W System.err: Caused by: android.system.ErrnoException: open failed: EMFILE (Too many open files)
03-18 13:44:18.239 28516 802 E art : ashmem_create_region failed for 'indirect ref table': Too many open files
03-18 13:44:18.243 28516 801 W System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
03-18 13:44:18.243 28516 801 W System.err: Caused by: android.system.ErrnoException: socket failed: EMFILE (Too many open files)
除了 FD 资源外,Native 内存不足也会导致创建线程失败,像这种资源使用超标导致的 Crash 问题堆栈是多种多样的,Crash 堆栈很可能只是压倒骆驼的最后一根稻草,所以基于 Crash 堆栈和异常类型的常规聚类方式往往起不到一个好的聚类效果,针对这一类问题有必要将这些问题按照资源使用的角度来分开,然后针对性的优化和解决。为了更全面地了解和解决一个业务 OOM 相关的问题,我们将 OOM 重新分为 Java OOM、FD OOM 和 Native OOM,并且扩充了原本的 OOM 率指标含义。

OOM 问题分类

Java OOM
Java OOM 是最常见的 OOM 问题,通常由于 Java 堆内存不足导致。当应用程序请求的内存超过 Java 堆的可用空间时,会抛出 java.lang.OutOfMemoryError 异常。这可能是由于 Java 内存泄漏、大对象分配、大图等原因引起的。
java.lang.OutOfMemoryError
Failed to allocate a 176 byte allocation with 5025912 free bytes and 4908KB until OOM, target footprint 536870912, growth limit 536870912; giving up on allocation because <1% of heap free after GC.

java.lang.OutOfMemoryError: Failed to allocate a 176 byte allocation with 5025912 free bytes and 4908KB until OOM, target footprint 536870912, growth limit 536870912; giving up on allocation because <1% of heap free after GC.
java.util.Arrays.copyOf(Arrays.java:3766)
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:125)
java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
java.lang.StringBuilder.append(StringBuilder.java:137)
......
FD OOM
FD OOM 是指文件描述符资源使用超标导致的 OOM 问题。每个进程在运行时都有一定数量的文件描述符可用,用于打开、读取和写入文件等操作。
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Crash type: 'native'
Start time: '2024-03-18T12:32:49.406+0800'
Crash time: '2024-03-18T16:09:33.763+0800'
App version: 'x.x.x.x'
Rooted: 'No'
API level: '27'
Build fingerprint: 'OPPO/R11/R11:8.1.0/OPM1.171019.011/1575877917:user/release-keys'
ABI: 'arm64'
pid: 9096, tid: 18434, name: HalleyTempTaskT  >>> com.tencent.xxxxx <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'Could not make wake event fd: Too many open files'
    x0  0000000000000000  x1  0000000000004802  x2  0000000000000006  x3  0000000000000008
    x4  0000007dce36f588  x5  0000007dce36f588  x6  0000007dce36f588  x7  0000007dce36f588
    x8  0000000000000083  x9  0000000010000000  x10 0000007dce36e9b0  x11 cd4becaebf0bf9bc
    x12 cd4becaebf0bf9bc  x13 0000000000000020  x14 ffffffffffffffdf  x15 cd4becaebf0bf9bc
    x16 0000000a2bfd9fa8  x17 0000007ed34b7540  x18 0000000000000001  x19 0000000000002388
    x20 0000000000004802  x21 0000000000000083  x22 000000007018bbc0  x23 0000000000004802
    x24 0000000000000001  x25 00000000701d41f0  x26 0000000015f00088  x27 0000000015f000b0
    x28 0000000015f00100  x29 0000007dce36e9f0
    sp  0000007dce36e9b0  lr  0000007ed3460770  pc  0000007ed3460798

backtrace:
#00 pc 000000000001e798  /system/lib64/libc.so (abort+120)
#01 pc 0000000000008348  /system/lib64/liblog.so (__android_log_assert+296)
#02 pc 000000000001542c  /system/lib64/libutils.so (_ZN7android6LooperC1Eb+308)
#03 pc 000000000011b5e0  /system/lib64/libandroid_runtime.so (_ZN7android18NativeMessageQueueC1Ev+160)
#04 pc 000000000011bebc  /system/lib64/libandroid_runtime.so (_ZN7androidL34android_os_MessageQueue_nativeInitEP7_JNIEnvP7_jclass+28)
#05 pc 000000000065b420  /system/framework/arm64/boot-framework.oat (android.os.Binder.clearCallingIdentity [DEDUPED]+144)
#06 pc 0000000000c8731c  /system/framework/arm64/boot-framework.oat (android.os.HandlerThread.run+332)
#07 pc 000000000054ad88  /system/lib64/libart.so (art_quick_invoke_stub+584)
#08 pc 00000000000dcf74  /system/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+200)
#09 pc 000000000046d6a0  /system/lib64/libart.so (_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_8ArgArrayEPNS_6JValueEPKc+100)
#10 pc 000000000046e8cc  /system/lib64/libart.so (_ZN3art35InvokeVirtualOrInterfaceWithJValuesERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_jmethodIDP6jvalue+836)
#11 pc 0000000000496e4c  /system/lib64/libart.so (_ZN3art6Thread14CreateCallbackEPv+1120)
#12 pc 0000000000074d74  /system/lib64/libc.so (_ZL15__pthread_startPv+36)
#13 pc 000000000001fce4  /system/lib64/libc.so (__start_thread+68)
Native OOM
Native OOM 是指本地内存地址空间使用超标导致的 OOM 问题。在 Android 开发中,应用程序可以使用本地代码(如 C/C++)来执行一些高性能的任务。如果本地代码中存在内存泄漏或者大量的本地内存分配,就会导致本地内存地址空间耗尽,从而引发 Native 内存分配相关的 OOM 问题,常见的像 mmap 失败或者显存爆了的问题。
03-18 14:39:31.424 9236 30371 W .tencent.xxx: Large object allocation failed: Failed anonymous mmap(0x0, 2101248, 0x3, 0x22, -1, 0): Out of memory. See process maps in the log.
03-18 14:39:31.437 9236 30371 W .tencent.xxx: Throwing OutOfMemoryError "Failed to allocate a 2097172 byte allocation with 24542160 free bytes and 283MB until OOM, target footprint 264041288, growth limit 536870912" (VmSize 4039036 kB)
03-18 14:39:32.128 9236 4592 D CCodecBufferChannel: [c2.mtk.hevc.decoder#869] DEBUG: elapsed: mInputMetEos 20, hasPendingOutputsInClient 0, n=1 [in=4 pipeline=0 out=16]
03-18 14:39:32.492 9236 30526 E CursorWindow: Failed mmap: Out of memory

03-18 15:42:31.466 18578 18864 W Adreno-GSL: <sharedmem_gpuobj_alloc:2713>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
03-18 15:42:32.351 18578 18864 W Adreno-GSL: <sharedmem_gpuobj_alloc:2713>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
03-18 15:42:33.279 18578 18864 W Adreno-GSL: <sharedmem_gpuobj_alloc:2713>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
03-18 15:42:33.321 18578 18864 E OpenGLRenderer: GL error: Out of memory!

指标分析

在 OOM 率指标体系中,java.lang.OutOfMemoryError 只是其中的一种,我们可以称作为 Java OOM 率,除了 Java OOM 率之外,这次我们增加了 FD OOM 率、Native OOM 率,分别对应的是因 FD 资源导致的崩溃问题概率和因进程 Native 内存导致的崩溃问题概率。新的 OOM 率体系只需要升级到4.3.2之后的版本就可以正常体验,不需要业务端做任何修改,其入口在 OOM 类型里面。


趋势分析

OOM 的趋势分析和 FOOM 的趋势分析类似,详情请参见 FOOM 指标分析

问题列表

OOM 的问题列表和崩溃监控模块类似,详情请参见 崩溃问题列表
说明:
重新定义 OOM 率和将 OOM 问题细分为不同类型的好处在于更准确地定位和解决 OOM 问题。通过监控和分析不同类型的 OOM 率,我们可以更好地了解应用程序在不同方面的资源使用情况,从而采取相应的优化措施。
Java OOM 率:通过监控 Java OOM 率,可以促使我们积极去发现内存泄漏问题、优化垃圾回收策略、调整堆内存大小等,以减少 Java OOM 问题的发生。
FD OOM 率:通过监控 FD OOM 率,可以帮助我们发现文件描述符的泄漏或滥用情况,及时关闭不再使用的文件描述符,以避免 FD OOM 问题的发生。
Native OOM 率:通过监控 Native OOM 率,可以帮助我们发现本地代码中的内存泄漏或者过度分配问题,及时释放不再使用的本地内存,以避免Native OOM 问题的发生。
通过细分和监控不同类型的 OOM 率,我们可以更精确地定位和解决 OOM 问题,提高应用程序的稳定性和性能,另外针对几种不同的 OOM 问题,B平台也已经分别提供了相应的监控功能,欢迎使用。

问题详情

OOM 的问题列表和崩溃监控模块类似,详情请参见 崩溃问题详情