Java 8 lambda的一个编译bug

最近利用github action向Maven中央仓库发布企业微信SDK时会失败,从日志中发现是系统资源耗尽了,日志如下:

php 复制代码
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 35 source files with javac [debug target 8] to target/classes


The system is out of resources.
Consult the following stack trace for details.
java.lang.StackOverflowError
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
	at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1624)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:576)
	at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2435)
    ............

编译的环境是 Java 8

应该是递归引用导致OOM了,但是仔细REVIEW了代码并没有发现问题。困扰了一个多月。今天终于解决了,问题是由下面的代码引起的:

java 复制代码
public final class WecomUserAgent {
    /**
     * 版本号
     */
    public static final String VERSION = "1.2.4";
    /**
     * UserAgent
     */
    public static final String WECOM_USER_AGENT= "Wecom/" + VERSION + Optional.ofNullable(System.getProperty("java.version"))
            .map(javaVersion -> " Java/" + javaVersion)
            .orElse("");
 
    private WecomUserAgent() {
    }
}

根本的原因在于上面的静态变量WECOM_USER_AGENT

Java 8 中,被标记final 的静态属性初始化时如果在字符串拼接中使用了lambda流操作,可能导致javac递归,最终堆栈溢出。该问题在Java 9 得到了修复 Bug报告:bugs.openjdk.org/browse/JDK-...

解决方法

升级JDK

升级JDK到9+ 就可以直接解决这个问题。

兼容写法

如果无法直接升级JDK,可以使用静态块来初始化:

java 复制代码
public final class WecomUserAgent {
    /**
     * 版本号
     */
    public static final String VERSION = "1.2.4";
    /**
     * UserAgent
     */
    public static final String WECOM_USER_AGENT;

    static {
        WECOM_USER_AGENT = "Wecom/" + VERSION + Optional.ofNullable(System.getProperty("java.version"))
                .map(javaVersion -> " Java/" + javaVersion)
                .orElse("");
    }

    private WecomUserAgent() {
    }
}
相关推荐
mldong5 小时前
给若依加审批流,不用 Flowable
java·spring boot·架构
Wang's Blog6 小时前
Java框架快速入门: Spring Security+OAuth2之数据库和实体类的RBAC改造
java·数据库·spring
讳疾忌医丶7 小时前
深度拆解 RocksDB 内核:基于 C++17 的 FIFO 调度状态机与温度阶梯自愈设计
java·c++·算法·架构
郑州光合科技余经理7 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
毅炼7 小时前
Rover-Suite 开源发布:轻量服务注册中心与网关一体化方案
java·后端·系统架构·gateway
梦想平凡8 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
sunshine22 girl9 小时前
Idea中如何搜索
java·ide·intellij-idea
猫吻鱼9 小时前
【AI 01】【Spring AI 基础使用】
java·spring·spring ai
梨涡泥窝9 小时前
基于SSM的校园二手交易平台的设计与实现
java·tomcat
许彰午10 小时前
44-useRowSet镜像实现
java·低代码·架构