惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
Recorded Future
Recorded Future
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
博客园 - Franky
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
T
Troy Hunt's Blog
The Register - Security
The Register - Security
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
S
Security Affairs
博客园 - 司徒正美
S
Schneier on Security
I
InfoQ
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
腾讯CDC
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
V
Vulnerabilities – Threatpost
C
Check Point Blog
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cisco Blogs
Schneier on Security
Schneier on Security
O
OpenAI News
K
Kaspersky official blog

美团技术团队

美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团发布基于 N-gram 全新模型:嵌入扩展新范式,实现轻量化 MoE 高效进化 2025美团技术年货,「马」上到来 多维创新打造强泛化智能体模型,LongCat-Flash-Thinking-2601技术报告发布 美团 EvoCUA 刷新开源 SOTA,会用电脑还会持续进化的智能体! 美团 LongCat-Flash-Thinking-2601 发布,工具调用能力登顶开源 SOTA! KuiTest:基于大模型通识的 UI 交互遍历测试 AAAI 2026 | 美团技术团队学术论文精选 2025 | 美团技术团队热门技术文章汇总 美团 LongCat-Video-Avatar 正式发布,实现开源 SOTA 级拟真表现 大模型剪枝新范式:先浓缩,再剪枝——DenoiseRotator技术解读 LongCat 上线 AI 生图!精准高效,AI 创作不设限 美团发布 LongCat-Image 图像生成模型,编辑能力登顶开源 SOTA AI Coding与单元测试的协同进化:从验证到驱动 R-HORIZON:探索长程推理边界,复旦NLP&美团LongCat联合提出LRMs能力评测新框架 美团 LongCat 发布 AMO-Bench:突破 AIME 评测饱和困境,重新定义 LLM 数学上限 美团 LongCat Interaction 团队发布大模型交互系统技术报告 WOWService 美团 LongCat 团队发布全模态一站式评测基准 UNO-Bench 美团开源LongCat-Audio-Codec,高效语音编解码器助力实时交互落地 NeurIPS 2025 | 美团技术团队论文精选 LongCat-Flash-Omni正式发布并开源:开启全模态实时交互时代 美团 LongCat 团队发布 VitaBench:基于复杂生活场景的交互式 Agent 评测基准 LongCat-Video 视频生成模型正式发布,探索世界模型的第一步 ICCV 2025 | 美团论文精选及多模态推理竞赛冠军方法分享 从0到1建设美团数据库容量评估系统 可验证过程奖励在提升大模型推理效率中的探索与实践 LongCat-Flash-Thinking 正式发布,更强、更专业,保持极速! 开源 | InfiniteTalk:无限长虚拟人视频生成的新范式 美团正式发布并开源 LongCat-Flash-Chat,动态计算开启高效 AI 时代 美团 M17 团队开源 Meeseeks 评测集:揭秘大模型的“听话”能力 可信实验白皮书系列08:开放式分析引擎 | 附PDF合集 美团智能头盔研发实践系列02:软件功能篇 美团智能头盔研发实践系列01:硬件设计篇 ACL 2025 | 美团技术团队论文精选 美团开源OIBench与CoreCodeBench:揭示大模型编程能力的真实水平 可信实验白皮书系列07:高阶实验工具 开源 | MeiGen-MultiTalk:基于单张照片实现多人互动演绎 可信实验白皮书系列06:观察性研究 JDK高版本特性总结与ZGC实践 可信实验白皮书系列05:准实验 可信实验白皮书系列04:随机轮转实验 可信实验白皮书系列03:随机对照实验 可信实验白皮书系列02:AB实验基础 可信实验白皮书系列01:从0到1的方法论与实践指南 MTGR:美团外卖生成式推荐Scaling Law落地实践 OR算法+ML模型混合推理框架架构演进 ICLR&CVPR 2025美团技术团队论文精选 行为正则化与顺序策略优化结合的离线多智能体学习算法 预测技术在美团弹性伸缩场景的探索与应用 美团技术年货 | 600+页电子书,算法、工程、测试、数据、安全系列大合集 鸿蒙应用签名实操及机制探究 2024 | 美团技术团队热门技术文章汇总 AutoConsis:UI内容一致性智能检测 CIKM 2024 | 美团技术团队精选论文解读 大前端:如何突破动态化容器的天花板? KDD 2024 OAG-Challenge Cup赛道三项冠军技术方案解读 新一代实验分析引擎:驱动履约平台的数据决策 大众点评技术部包揽KDD 2024 OAG-Challenge Cup赛道全部3项冠军 ACL 2024 | 美团技术团队精选论文解读 KDD 2024 | 美团技术团队精选论文解读 基本功 | 一文讲清多线程和多线程同步 小程序可测性能力建设与实践 SIGIR 2024 | 美团技术团队精选论文解读 Spark向量化计算在美团生产环境的实践 CVPR 2024 | 美团技术团队精选论文解读 基于多模态信息抽取的菜品知识图谱构建 DDD在大众点评交易系统演进中的应用 美团外卖基于GPU的向量检索系统实践 美团大规模KV存储挑战与架构实践 基于接口数据变异的App健壮性测试实践 美团技术年货 | 600+页电子书,前端、后端、算法、测试、运维系列大合集 美团RASP大规模研发部署实践总结 2023 | 美团技术团队热门技术文章汇总 美团到店终端从标准化到数字化的演进之路 AIOps在美团的探索与实践——事件管理篇 美团技术博客十周年,感谢一路相伴 基于UI交互意图理解的异常检测方法 如何利用「深度上下文兴趣网络」提升点击率? 基于模式挖掘的可靠性治理探索 代码变更风险可视化系统建设与实践 美团多场景建模的探索与实践 MJDK 如何实现压缩速率的 5 倍提升? 如何提供一个可信的AB测试解决方案 KDD 2023 | 美团技术团队精选论文解读 美团前端研发框架Rome实践和演进趋势 斩获CVPR 2023竞赛2项冠军|美团街景理解中视觉分割技术的探索与应用 MySQL自治平台建设的内核原理及实践(下) CVPR 2023 | 美团技术团队精选论文解读 超大规模数据库集群保稳系列之三:美团数据库容灾体系建设实践 超大规模数据库集群保稳系列之二:数据库攻防演练建设实践 Robust 2.0:支持Android R8的升级版热修复框架 超大规模数据库集群保稳系列之一:高可用系统 一次「找回」TraceId的问题分析与过程思考 基于AI+数据驱动的慢查询索引推荐 SOTA!目标检测开源框架YOLOv6 3.0版本来啦
Android OOM案例分析
军慧 · 2017-04-14 · via 美团技术团队

在Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或者Error都要难一些,主要是由于错误产生的root cause不是很显而易见。由于没有办法能够直接拿到用户的内存dump文件,如果错误发生在线上的版本,分析起来就会更加困难。本文从一个具体的案例切入,介绍OOM分析的思路及相关工具的使用。

案例背景

在美团App 7.4~7.7版本期间,美食业务的OOM数量居高不下,远高于历史水平,主要都是DECODE本地的资源出错。

图中OOM数量为各版本发版后第一个月的统计量,包含新发版本及历史版本。对比了同时期其他业务的情况,也有类似OOM。由于美食业务的访问量占美团App的比重较大,因此,OOM的数量相对其他业务也多一些。

思路方案

在问题较为严重的7.6~7.7版本期间,团队对OOM频现的原因有过各种猜测。笔者怀疑过是否是业务上某些修改引起的,例如头图尺寸变大,或者是由页面模块加载方式引起的等等。但这些与OOM问题出现的时间并不吻合。其次也怀疑过是否由某些ROM的Bug导致,但此推断缺乏有力的证据支撑。因此,要找到OOM的root cause,根本途径还是找到谁占的内存最多,然后再根据具体case具体分析,为什么占了这么多。

采集用户手机内存信息

要分析内存的占用,需要内存的dump文件,但是dump文件一般都比较大,让用户配合上传dump文件不合适。所以希望能够运行时采集一些内存的特征然后随着crash日志上报上来。当用户发生OOM时,dump出用户的内存,然后基于com.squareup.haha:haha:2.0.3分析,得到一些关键数据(内存占用最多的实例及所占比例等)。但这个方案很快就被证明是不可行的。主要基于下面几个原因: - 需要引入新的库。 - dump和分析内存都很耗时,效率难以接受。 - OOM时内存已经几乎耗尽,再加载内存dump文件并分析会导致二次OOM,得不偿失。

模拟复现OOM

采集用户手机内存信息的方案不可行,那么只能采取复现用户场景的方式。由于发生OOM时,用户操作路径的不确定性,无法精确复现线上的OOM,因此采取模拟复现的方式,最终发生OOM时的栈信息基本一致即可。为了能够尽量模拟用户发生OOM的场景,需要基本条件基本一致,即用户使用的手机的各种相关参数。

挖掘OOM特征

分析7.4以来的OOM,列出发生OOM的机器的特征,主要是内存和分辨率,适当考虑其它因素例如系统版本。

| 机型 | 内存 | 分辨率 | OS | stack log | | – | – | – | – | – | | OPPO N1(T/W) | 2G | 1920*1080 | 4.2.2 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)| | HM 2LTE-CMCC | 1G | 1280*720 | 4.4.4 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) | | Newman CM810 | 2G | 1920*1080 | 4.4.4 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) | | LGL22 | 2G | 1830*1080 | 4.2.2 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) | | OPPO X909 | 2G | 1920*1080 | 4.2.2 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) | | Lenovo K900 | 2G | 1920*1080 | 4.2.2 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) | | GiONEE E6 | 2G | 1920*1080 | 4.2.1 | java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) |

这些特征可以总结为:内存一般,分辨率偏高,OOM的堆栈log基本一致。其中,OPPO N1(T/W)上所发生的OOM比重较高,约为65%,因此选定这款机器作为复现OOM的机器。

关键数据(内存dump文件)

需要复现OOM然后获取内存dump。思路是采取内存压力测试,让问题暴露的快速且充分。具体方案为: - 选取图片资源多且较为复杂的页面,比如美食的POI详情页。 - 加载30次该页面,为了增加OOM的几率,30个POI页面的ID是不同的。

OOM发生后,使用Android Studio自带的Android Monitor dump出HPROF文件,然后使用SDK中的hprof-conv(位于sdk_root/platform-tools)工具转换为标准的Java堆转储文件格式,这样可以使用MAT(Eclipse Memory Analyzer)继续分析。

切到histogram视图,按shadow heap降序排列。

选取byte数组,右击->list objects->with incoming references,降序排列可以看到有很多大小一致的byte[]实例。

右击其中一个数组->Path to GC Roots-> exclude xxx references

如上图所示,这些byte[]都是系统的EdgeEffect的drawable所持有,drawable对应的bitmap占用的空间为1566 * 406 * 4 = 2543184,与byte数组的大小一致。

再看另外一个:

这些byte[]是被App的一个背景图所持有,如下图:

通过ImageView的ID(如图)及build目录下的R.txt反查可知该ImageView的ID名称,即可知其设置的背景图的大小为720 * 200(xhdpi),加载到内存并考虑density,size刚好是1080 * 300 * 4 = 1296000,与byte数组大小一致。

数据分析

为什么会出现这些大小一致的byte数组,或者说,为什么会创建多份EdgeEffect的drawable?查看EdgeEffect的源码(4.2.2)可知,其drawable成员也是通过Resources.getDrawable系统调用获取的。

/**
 * Construct a new EdgeEffect with a theme appropriate for the provided context.
 * @param context Context used to provide theming and resource information for the EdgeEffect
 */
public EdgeEffect(Context context) {
    final Resources res = context.getResources();
    mEdge = res.getDrawable(R.drawable.overscroll_edge);
    mGlow = res.getDrawable(R.drawable.overscroll_glow);

        ******

    mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f);
    mInterpolator = new DecelerateInterpolator();
}

ImageView(View)获取background对应的drawable的过程类似。

for (int i = 0; i < N; i++) {
    int attr = a.getIndex(i);
    switch (attr) {
        case com.android.internal.R.styleable.View_background:
            background = a.getDrawable(attr); // TypedArray.getDrawable
            break;
        ******
    }
}

不论是Resources.getDrawable还是TypedArray.getDrawable,最终都会调用Resources.loadDrawable。继续看Resources.loadDrawable的源码,发现的确是使用了缓存。对于同一个drawable资源,系统只会加载一次,之后都会从缓存去取。

既然drawable的加载机制并没有问题,那么drawable所在的缓存实例或者获取drawable的Resources实例是否是同一个呢?通过下面的代码,打印出每个Activity的Resources实例及Resources实例的drawable cache。

//noinspection unchecked
LongSparseArray<WeakReference<Drawable.ConstantState>> cache = (LongSparseArray<WeakReference<Drawable.ConstantState>>) Hack.into(Resources.class).field("mDrawableCache").get(getResources());
Object appCache = Hack.into(Resources.class).field("mDrawableCache").get(getApplication().getResources());
Log.e("oom", "Resources: {application=" + getApplication().getResources() + ", activity=" + getResources() + "}");
Log.e("oom", "Resources.mDrawableCache: {application=" + appCache + ", activity=" + cache + "}"); 

这也进一步解释了另外一个现象,即这些大小相同的数组的个数基本和启动Activity的数量成正比。

通过数据分析可知,这些drawable之所以存在多份,是因为其所在的Resources实例并不是同一个。进一步debug可知,Resources实例存在多个的原因是开启了标志位sCompatVectorFromResourcesEnabled。 虽然最终造成OOM突然增多的原因只是开启一个标志位,但是这也告诫大家阅读API文档的重要性,其实很多时候API的使用说明已经明确告知了使用的限制条件甚至风险。

7.8版本关闭了此标志,发版后第一个月的OOM数量(包含历史版本)为153,如下图。

其中新版本发生的OOM数量为22。

总结

对于线上出现的OOM,如何分析和解决可以大致分为三个步骤:

  1. 充分挖掘特征。在挖掘特征时,需要多方面考虑,此过程更多的是猜测怀疑,所以可能的方面都要考虑到,包括但不限于代码改动、机器特征、时间特征等,必要时还需要做一定的统计分析。
  2. 根据掌握的特征寻找稳定的复现的途径。一般需要做内存压力测试,这样比较容易达到OOM的临界值,只是简单的一些正常操作难以触发OOM。
  3. 获取可分析的数据(内存dump文件)。利用MAT分析dump文件,MAT可以方便的按照大小排序实例,可以查看某些实例到GC ROOT的路径。

作者简介

军慧,美团点评Android高级工程师,2015年加入原美团,负责美团点评到店餐饮业务美团Android App的开发工作。

到店餐饮技术部交易与信息技术中心,负责美团点评美食用户端业务,服务于数以亿计用户,通过更好的榜单、真实的评价和完善的信息为用户提供更好的决策支持,致力于提升用户体验;同时承载所有餐饮商户端线上流量,为餐饮商户提供多种营销工具,提升餐饮商户营销效率,最终达到让国人“Eat Better、Live Better”的美好愿景!我们的团队包含且不限于Android、iOS、FE、Java、PHP等技术方向,已完备覆盖前后端技术栈。只要你来,就能点亮全栈开发技能树。