当前位置: 首页 > news >正文

Android13 编译ninja失败:exit status 137 的内存优化实战

1. 遇到exit status 137时的排查思路

第一次看到ninja编译报exit status 137时,我也是一头雾水。明明机器配置不差,32G内存的Ubuntu服务器,怎么会在编译Android13时出现内存不足?后来发现这个问题在大型项目编译中其实很常见,特别是当系统同时运行多个内存密集型任务时。

通过查看out/soong.log,我发现几个关键线索:ninja进程的maxrss(实际使用的物理内存)达到了17723MB,而soong bootstrap阶段更是飙到22311MB。这说明编译过程中的内存占用已经接近甚至超过了物理内存上限。这时候Linux的OOM-killer就会出手,强制终止内存占用最高的进程,导致编译中断。

更隐蔽的情况是:有时候系统看似还有可用内存,但实际上已经被缓存和buffer占满。这时候可以用free -h命令查看真实的内存使用情况。如果available内存所剩无几,就要警惕OOM风险了。

2. 临时解决方案:调整ninja并发数

最快速的缓解方法是降低ninja的并行编译任务数。默认情况下,ninja会根据CPU核心数自动设置并发数(比如16核CPU就是-j16),但这可能超出内存承受能力。

我常用的几种调整方式:

# 保守方案:减半并发数 ninja -j8 # 更保守方案:固定为4个任务 ninja -j4 # 动态方案:根据内存大小计算 # 假设每个任务平均消耗2GB内存,32G物理内存留8G给系统 ninja -j$(( (32 - 8) / 2 ))

实测发现,将并发数从16降到8后,虽然编译时间增加了约30%,但成功避开了OOM问题。这里有个经验值:AOSP完整编译时,每个ninja任务平均需要1.5-2GB内存空间。

3. 长效解决方案:配置swap空间

临时调整并发数只是权宜之计,更彻底的方案是配置合理的swap空间。我的Ubuntu服务器原本只有2GB swap,这对于Android编译远远不够。

以下是创建8GB swap的详细步骤:

# 禁用现有swap sudo swapoff -a # 创建swap文件(注意需要root权限) sudo dd if=/dev/zero of=/swapfile bs=1G count=8 sudo chmod 600 /swapfile # 格式化并启用 sudo mkswap /swapfile sudo swapon /swapfile # 验证结果 free -h

为了让配置永久生效,还需要编辑/etc/fstab文件:

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

这里有个细节:swapfile最好放在SSD上,如果是机械硬盘,频繁的swap操作会显著拖慢编译速度。我曾经在HDD上配置过32GB swap,结果编译时间比物理内存方案慢了3倍。

4. 系统级内存优化技巧

除了调整swap,还可以通过这些方法优化内存使用:

清理缓存内存

# 释放pagecache sudo sync; echo 1 | sudo tee /proc/sys/vm/drop_caches # 释放dentries和inodes sudo sync; echo 2 | sudo tee /proc/sys/vm/drop_caches # 释放所有缓存 sudo sync; echo 3 | sudo tee /proc/sys/vm/drop_caches

调整系统swappiness(默认值60可能过高):

# 查看当前值 cat /proc/sys/vm/swappiness # 临时设置为更保守的值 sudo sysctl vm.swappiness=30 # 永久生效 echo 'vm.swappiness=30' | sudo tee -a /etc/sysctl.conf

使用cgroup限制内存(适合多用户环境):

# 创建内存限制组 sudo cgcreate -g memory:android_build # 限制最大内存为24GB echo 24G | sudo tee /sys/fs/cgroup/memory/android_build/memory.limit_in_bytes # 在cgroup中运行编译 cgexec -g memory:android_build ninja -j16

5. 进阶:分析内存使用热点

当问题特别棘手时,需要更深入分析内存使用情况。我的诊断工具箱里有这些利器:

通过/var/log/syslog定位OOM事件

grep -i oom /var/log/syslog

使用top动态监控

top -o %MEM

使用smem分析进程内存

smem -s swap -r -k -P ninja

使用valgrind进行内存分析(需要重新编译):

valgrind --tool=massif ninja -j4

通过这些工具,我发现一个有趣的现象:某些C++模块的模板实例化会消耗异常多的内存。针对这种情况,可以在模块的Android.bp中添加:

cc_defaults { name: "module_memory_optimize", cflags: [ "-ftemplate-depth=128", // 降低模板实例化深度 "-fconstexpr-depth=64", // 限制constexpr递归 ], }

6. 编译环境的最佳实践

经过多次踩坑,我总结出这些Android编译环境配置建议:

  1. 物理内存与swap的比例:32G物理内存建议配8-16G swap,64G内存配16-32G swap。swap过小无法缓解OOM,过大会影响性能。

  2. 文件系统选择:ext4比xfs更适合频繁的编译操作,因为其inode缓存机制更高效。我曾经在xfs上遇到inode耗尽导致编译失败的情况。

  3. 避免内存碎片:长时间运行的编译服务器建议定期重启,特别是当观察到cat /proc/buddyinfo显示内存碎片严重时。

  4. 隔离编译环境:使用Docker或chroot创建干净的编译环境,避免其他服务干扰。我常用的Docker配置:

FROM ubuntu:18.04 RUN sysctl -w vm.overcommit_memory=1 && \ echo 1 > /proc/sys/vm/overcommit_memory
  1. 监控方案:设置简单的内存监控脚本:
while true; do date >> mem.log free -h >> mem.log ps -eo pid,comm,%mem --sort=-%mem | head -10 >> mem.log sleep 60 done

7. 针对Android13的特殊优化

Android13引入的某些新特性会额外增加内存压力:

针对ART模块的优化

# 在eng模式下关闭某些优化 export SOONG_CONFIG_art_module_compress_dex=false export SOONG_CONFIG_art_module_dexpreopt=false

调整dex2oat并行度

art_defaults { dex2oat_num_threads: 4, // 默认是CPU核心数 }

关闭部分调试符号(适合userdebug构建):

export USE_DEX_DEBUG=false export USE_SYMBOLIZE=false

对于cuttlefish等模拟器构建,还需要特别注意:

# 减少模拟器相关进程的内存占用 export CUTTLEFISH_AVF_MEMORY=2048 export CUTTLEFISH_AVF_CPUS=4

8. 硬件选购建议

如果条件允许,这些硬件配置能显著改善编译体验:

  1. 内存带宽比容量更重要:四通道DDR4-3200比双通道DDR4-2666的实际编译速度快15-20%,即使总容量相同。

  2. SSD选择:建议PCIe 4.0 NVMe SSD,持续写入速度不低于3000MB/s。我测试过,三星980 Pro编译AOSP比SATA SSD快40分钟。

  3. CPU选择:大缓存比多核心更有帮助。比如Core i9-13900K(36MB L3)比线程撕裂者3970X(128MB L3)编译速度慢,尽管后者核心数更少。

  4. 散热设计:持续编译时CPU会长时间满载,好的散热能避免降频。我的编译服务器换了Noctua NH-D15后,编译时间缩短了7%。

http://www.jsqmd.com/news/520440/

相关文章:

  • 利用 Hough 变换处理量测得到的含杂波的二维坐标,解决多目标航迹起始问题(Matlab代码实现)
  • 造相-Z-Image-Turbo 结合Python爬虫:自动构建特定风格训练数据集
  • DeOldify与数据库联动:开发基于MySQL的图片处理任务管理系统
  • Vivado工程移植血泪史:IP核被锁、路径丢失?手把手教你从源码重建自定义IP
  • 嵌入式系统中无库依赖的数据类型转换实战
  • JeeH:面向Cortex-M的轻量级消息驱动嵌入式运行时
  • 完全开源的物联网平台!一站式搭建,设备管理+数据可视化全搞定,覆盖智能能源/楼宇/城市多场景
  • 从黑白到彩色:WSL终端美化全攻略(含ls/grep高亮配置)
  • 利用Perturb and Observe(PO)实现光伏供电的直流-直流升压变换器的最大功率跟踪(Simulink仿真实现)
  • HY-MT1.5-1.8B快速上手:用chainlit创建翻译交互界面,简单易用
  • 解密Houdini VEX属性系统:从基础属性到自定义volume控制全指南
  • 别光重启了!深度排查苍穹外卖项目Nginx代理与前后端联调401/404错误
  • 嵌入式轻量级多轨WAV混音播放器htcw_player
  • Stable Yogi Leather-Dress-Collection完整指南:LoRA目录结构规范与热重载机制
  • Qwen3-Reranker-0.6B保姆级教学:中文Query+英文Doc跨语言排序实操演示
  • Android Studio 2023.2.1 中 Gemini AI 的 7 个隐藏用法(附实战代码)
  • Qwen3-32B-Chat镜像参数详解:CUDA12.4+驱动550.90.07兼容性验证报告
  • 寻音捉影·侠客行显存优化技巧:长音频分段处理降低内存占用实战
  • C语言编译器APP:助力学习实践,编写超便捷,功能超丰富
  • 手把手教你用Unsloth微调DeepSeek-R1:从环境配置到解决AttributeError的完整避坑指南
  • AlienFX Tools终极指南:3大核心功能解锁Alienware设备个性化控制
  • 小白必看:黑丝空姐-造相Z-Turbo镜像使用常见问题与解决
  • Kazumi:5分钟打造你的专属动漫播放器,告别资源碎片化困扰
  • Linux无线网卡驱动终极指南:解决Realtek 8852CE连接问题的完整教程
  • Teensy硬件PWM深度解析:实时控制中的抖动消除与多通道同步
  • M5Stack嵌入式软键盘:基于状态机的轻量级文本输入方案
  • LangFlow轻松入门:无需编程基础,快速创建你的第一个LangChain应用
  • Qwen3-VL-8B图文理解效果展示:中文手写笔记识别+要点结构化提取
  • BtnEnhancer:嵌入式高可靠按键事件处理框架
  • 梦幻动漫魔法工坊提示词秘籍:写出让AI更懂你的动漫描述