对比官方 gradle 代码
我们首先 clone 官方代码 gradle,找到 DefaultCachedClasspathTransformer,
发现最新代码已经进行了修改,限制了线程的数量。改为跟 CPU 核心数挂钩。
而他是在什么时候进行了修改了,其实很简单,我们可以借助 git 命令,找到他属于哪一个 TAG.
git tag --contains 2a1e74166bc82607e15de78002ef56582b34af0d
很快我们发现了,他在 gradle 7.0 上面对线程池的线程数进行了限制,改为跟 CPU 核心数挂钩。
思考
先来思考一个问题,可能很多人有同样的疑问。
为什么有的机器没出现有的机器会出现,⽐如我同事的mac就没有发现
我么先来看一下 java tranfrom 线程是干什么用的,
我们可以看这里的代码
org.gradle.internal.classpath.DefaultCachedClasspathTransformer.TransformFile#schedule
跟踪下去,你会发现主要是一些 IO 读取操作。
如果说你的机器磁盘性能比较好,那么 IO 读取比较快,线程干完工作之后,就会自动销毁,出现这样现象的可能性会比较低。如果磁盘性能较差,出现的可能性会比较大。
问题解决
既然怀疑问题是因为这里的线程数引起的,于是第一时间我们想到了几种方法
- 反射修改线程池的数量
- 升级 gradle 版本
于是,我们跟中代码,试试反射能不能修改代码,但很快,我们发现,并没有找到一个好的 hook 点,无法修改。
可能有人会想到 epic,没错,刚开始我也想用 epic。但是 epic 是基于安卓 ART 虚拟机的,而我们编译的时候,是基于 JVM 的,epic 是无法使用的。
接着我们尝试了第二种方法,尝试升级 gradle 版本到 7.0,折腾了一fang之后,发现升级要适配的东西还是蛮多的,一下子无法解决
- maven repo 仓库设置 allowInsecureProtocol
- grrovy 版本冲突
- JavaParser 错误
- …
总之,错误是解决完一个接着一个,还是挺多坑的
柳暗花明又一村
跟汉光爷讨论之后,汉光爷说能不能自己编译一个版本出来。他在官网上找到了编译 gradle 版本的方法
编译完成之后,上传到 CC 的 S3 服务器上面,我们在 gradle-wrapper.properties 下面修改,替换成自己的 gradle 版本
#Thu May 30 18:31:45 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists # 解决编译线程数过多,导致 OOM 的问题 distributionUrl=https://xx.cn/static/gradle/gradle-6.9.3-all.zip
再次编译,可以看到 jar transform 相关的线程数,最多变为 8 了,因为我的电脑是 8 核单核心的。
到此,我们对 gradle jar transfrom thread 的线程数进行了限制,合理应该是不会再出现 OOM 了,如果还会出现,可以保留现场,找我或者汉光看看。
总结
可以看到,我们这次的问题解决思路大概是这样的。
- 从 error 日志排查发现,很有可能跟 transfrom 相关
- 排查项目里面 transfrom 相关的,有没有 jar transform Thread 相关的
- dump JVM 内存,看线程相关的,观察 jar transform Thread 是否异常
- debug gradle assemble 任务,观察 线程名包括 jar transform Thread Thread 的调用堆栈
- 分析 调用堆栈,找到原因
- 结合 gradle 官方代码,查看问题是否已经解决
那有没有更快的方法呢?
其实如果一开始能确定是 gradle 问题的话,可以直接在 gradle 里面搜索字符串 jar transforms,然后再一步步反推,其实也是可以的。