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 bs1G count8 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.swappiness30 # 永久生效 echo vm.swappiness30 | 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 -j165. 进阶分析内存使用热点当问题特别棘手时需要更深入分析内存使用情况。我的诊断工具箱里有这些利器通过/var/log/syslog定位OOM事件grep -i oom /var/log/syslog使用top动态监控top -o %MEM使用smem分析进程内存smem -s swap -r -k -P ninja使用valgrind进行内存分析需要重新编译valgrind --toolmassif ninja -j4通过这些工具我发现一个有趣的现象某些C模块的模板实例化会消耗异常多的内存。针对这种情况可以在模块的Android.bp中添加cc_defaults { name: module_memory_optimize, cflags: [ -ftemplate-depth128, // 降低模板实例化深度 -fconstexpr-depth64, // 限制constexpr递归 ], }6. 编译环境的最佳实践经过多次踩坑我总结出这些Android编译环境配置建议物理内存与swap的比例32G物理内存建议配8-16G swap64G内存配16-32G swap。swap过小无法缓解OOM过大会影响性能。文件系统选择ext4比xfs更适合频繁的编译操作因为其inode缓存机制更高效。我曾经在xfs上遇到inode耗尽导致编译失败的情况。避免内存碎片长时间运行的编译服务器建议定期重启特别是当观察到cat /proc/buddyinfo显示内存碎片严重时。隔离编译环境使用Docker或chroot创建干净的编译环境避免其他服务干扰。我常用的Docker配置FROM ubuntu:18.04 RUN sysctl -w vm.overcommit_memory1 \ echo 1 /proc/sys/vm/overcommit_memory监控方案设置简单的内存监控脚本while true; do date mem.log free -h mem.log ps -eo pid,comm,%mem --sort-%mem | head -10 mem.log sleep 60 done7. 针对Android13的特殊优化Android13引入的某些新特性会额外增加内存压力针对ART模块的优化# 在eng模式下关闭某些优化 export SOONG_CONFIG_art_module_compress_dexfalse export SOONG_CONFIG_art_module_dexpreoptfalse调整dex2oat并行度art_defaults { dex2oat_num_threads: 4, // 默认是CPU核心数 }关闭部分调试符号适合userdebug构建export USE_DEX_DEBUGfalse export USE_SYMBOLIZEfalse对于cuttlefish等模拟器构建还需要特别注意# 减少模拟器相关进程的内存占用 export CUTTLEFISH_AVF_MEMORY2048 export CUTTLEFISH_AVF_CPUS48. 硬件选购建议如果条件允许这些硬件配置能显著改善编译体验内存带宽比容量更重要四通道DDR4-3200比双通道DDR4-2666的实际编译速度快15-20%即使总容量相同。SSD选择建议PCIe 4.0 NVMe SSD持续写入速度不低于3000MB/s。我测试过三星980 Pro编译AOSP比SATA SSD快40分钟。CPU选择大缓存比多核心更有帮助。比如Core i9-13900K36MB L3比线程撕裂者3970X128MB L3编译速度慢尽管后者核心数更少。散热设计持续编译时CPU会长时间满载好的散热能避免降频。我的编译服务器换了Noctua NH-D15后编译时间缩短了7%。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2439073.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!