项目升级到 AGP8 以后 一直有个问题,有一个 利用 jacoco 做代码覆盖率的插件 一直会导致项目编译失败,偶现,且 build 失败给出的 堆栈信息迷惑性很强,导致问题排查方向错了,最后发现是 FD 泄露导致的。

调整编译命令 给出更多报错信息以后 如下:


这里就很有迷惑性, 为什么报错在 r8 这里, 甚至堆栈里都明确给出了哪个类的哪个方法出现了问题
这让我的问题排查方向一直陷在 是字节码处理逻辑上的冲突导致, 然后就无用功了很久
最后是通过排除法 逐一把项目中所有字节码处理相关的场景1 个个去掉以后,定位到是某个 task 和 这个 jacoco 的不兼容导致
但是一时半会也没怀疑到跟文件描述符有关。
可以在 build task中 找到最后一个task,加个 doLast 的逻辑 while true 就可以 ,让 build 进程 一直 hang 在那里,方便我们查看进程情况

可以看出来这里 打开了 3600 多个文件实属不太正常
里面还有大量的 class 文件

最后修复了这个文件描述符泄露的问题以后,build 就成功了
理性猜测是agp8 以后,新增的 asm api 本质上是可以并行执行 aop task 的,这里面可能有更加严格的校验机制,导致 FD 泄露以后 校验机制出现异常 最后 build 失败,并且给出了错误的提示信息
脚本
用来统计 jvm 进程 所有文件描述符打开情况(指挥llm 写脚本是真的方便)
shell
#!/bin/bash
# 检查 'jps' 命令是否存在
if ! command -v jps &> /dev/null; then
echo "错误: 未找到 'jps' 命令。请确保 JDK 已安装并且其 bin 目录已添加到您的 PATH 环境变量中。"
exit 1
fi
# 检查 'lsof' 命令是否存在
if ! command -v lsof &> /dev/null; then
echo "错误: 未找到 'lsof' 命令。在 macOS 上,它通常是预装的。"
exit 1
fi
# 显示当前系统时间
echo "扫描时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "正在列出每个 JVM 进程打开的文件描述符详情..."
echo ""
# 使用 jps -l 获取 PID 和主类名
jps -l | while read -r pid name; do
# 如果进程在 jps 和 lsof 命令之间退出了,lsof 会报错,所以先检查进程是否存在
if ! ps -p "$pid" > /dev/null; then
continue
fi
# 如果 name 为空 (例如 jps 进程本身),给一个默认值
if [ -z "$name" ]; then
name="<jps_process>"
fi
# 先用 lsof 统计文件描述符总数
count=$(lsof -p "$pid" 2>/dev/null | wc -l)
# 如果进程没有打开任何文件(或已退出),则跳过
if [ "$count" -eq 0 ]; then
continue
fi
echo "=========================================================================================="
printf "PID: %-10s | Total FDs: %-8s | Main Class/JAR: %s\n" "$pid" "$count" "$name"
echo "------------------------------------------------------------------------------------------"
printf "%-15s %-8s %-12s %-10s %s\n" "COMMAND" "PID" "TYPE" "SIZE/OFF" "NAME"
echo "------------------------------------------------------------------------------------------"
# 使用 lsof 列出该进程打开的所有文件,并使用 awk 过滤掉不需要的列
# 只保留 COMMAND, PID, TYPE, SIZE/OFF, NAME 这几列
lsof -P -n -p "$pid" 2>/dev/null | awk 'NR>1 {printf "%-15s %-8s %-12s %-10s %s\n", $1, $2, $5, $7, $9}'
echo "=========================================================================================="
echo ""
done
echo "统计完成。"