且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

缓慢的JDK8编译

更新时间:2022-10-14 22:47:25

您已经注意到,在Java 8中有一个严重的性能回归,当涉及基于通用目标打字的重载解决方案。在你的情况下的原因之一可能是编译器需要从一个赋值类型中找到适当的方法。

  ResultVO<东西,东西> result = Mapping.convert(...); 
// heavy lookup here --------------------------- ^^^^^^^

如果你在控制代码生成器,并且不受向后兼容性约束,那么可能值得考虑避免重载 convert()方法。没有重载,编译器不必做重载分解工作,既不在你的映射代码内,也不在调用站点。这将肯定会快得多。



尝试1:在方法名称中使用参数类型:



  class Mapping {
public static ResponseItemVO convertResponseItem(ResponseItem pArg0){
if(pArg0 == null){
return null;
}
ResponseItemVO ret = new ResponseItemVO();
ret.setErrorDetails(pArg0.getErrorDetails());
ret.setResult(Mapping.convertResult(pArg0.getResult()));
ret.setIdentifier(Mapping.convertIdentifier(pArg0.getIdentifier()));
return ret;尝试2:通过将转换方法移动到其他地方,例如,将转换方法移动到其它地方。到 VO 类型

  class ResponseItemVO {
public static ResponseItemVO from(ResponseItem pArg0){
if(pArg0 == null){
return null;
}
ResponseItemVO ret = new ResponseItemVO();
ret.setErrorDetails(pArg0.getErrorDetails());
ret.setResult(ResultVO.from(pArg0.getResult()));
ret.setIdentifier(IdentifierVO.from(pArg0.getIdentifier()));
return ret;
}
}

或更好...

  class ResponseItem {
public ResponseItemVO toVO(){
ResponseItemVO ret = new ResponseItemVO();
ret.setErrorDetails(getErrorDetails());
ret.setResult(getResult()。toVO());
ret.setIdentifier(getIdentifier()。toVO());
return ret;
}
}


Trying to upgrade to JDK8 on a big project, compilation goes really slow on JDK8 compared to JDK7.

Running the compiler in verbose mode, JDK8 compiler stops at a big generated converter class(Mapping) for entities from server to client. The converter methods in several cases call other converter methods from the same Mapping class.

As a workaround tried to split the Mapping file into multiple files. This visibly improved performance when only compiling the Mapping class or it's containing project(projectA). But compile time was very slow for other projects which invoke converter methods from projectA.

Another workaround was to make all convert methods return null, not calling anything else. Again, the performance was good for projectA but not for depending projects.

ProjectA uses generics but since it is compatible with JDK6, which didn't have generalized type inference introduced, maybe it's another JDK8 bug that causes this slowdown.

So possibly out of context but for generalized type inference, some threads like below suggest an upgrade to JDK9. But since it's not yet released, it's not a viable option as upgrade. It'd be ideal if a backport of the fix would be done to JDK8. This was requested in the following *** thread but no reply from Oracle team yet.

Slow compilation with jOOQ 3.6+, plain SQL, and the javac compiler

I've attached 2 screenshots of how the heap looks in JDK7 vs JDK8. Could this be a cause for the JDK8 slowdown?

Thank you!

Update 20160314

The converter methods from Mapping class look like:

public static ResponseItemVO convert (ResponseItem pArg0){
         if(pArg0==null){
             return null;
         }
        ResponseItemVO ret = new ResponseItemVO();
        ret.setErrorDetails(pArg0.getErrorDetails());
        ret.setResult(Mapping.convert(pArg0.getResult()));
        ret.setIdentifier(Mapping.convert(pArg0.getIdentifier()));
        return ret;
    }

And the VO looks like:

public class ResponseItemVO extends ResultVO<IdentifierVO, DetailsVO >  {
    public ResponseItemVO() {}
}

JDK7 Heap: JDK8 Heap:

You've noticed already, there's a severe performance regression in Java 8 when it comes to overload resolution based on generic target typing. One of the reasons in your case might be the fact that the compiler needs to find the appropriate method from an assignment type

ResultVO<Something, Something> result = Mapping.convert(...);
// heavy lookup here ---------------------------^^^^^^^

If you're in control of the code generator, and not constrained by backwards compatibility, it might be worth thinking about avoiding the overloading of the convert() method. Without overloading, the compiler doesn't have to do the overload resolution work, neither inside of your mapping code, nor at the call site. This will certainly be much much faster.

Attempt 1: By using the parameter type in the method name:

class Mapping {
    public static ResponseItemVO convertResponseItem(ResponseItem pArg0){
        if (pArg0==null){
            return null;
        }
        ResponseItemVO ret = new ResponseItemVO();
        ret.setErrorDetails(pArg0.getErrorDetails());
        ret.setResult(Mapping.convertResult(pArg0.getResult()));
        ret.setIdentifier(Mapping.convertIdentifier(pArg0.getIdentifier()));
        return ret;
    }
}

Attempt 2: By moving the convert method elsewhere, e.g. into the VO type

class ResponseItemVO {
    public static ResponseItemVO from(ResponseItem pArg0){
        if (pArg0==null){
            return null;
        }
        ResponseItemVO ret = new ResponseItemVO();
        ret.setErrorDetails(pArg0.getErrorDetails());
        ret.setResult(ResultVO.from(pArg0.getResult()));
        ret.setIdentifier(IdentifierVO.from(pArg0.getIdentifier()));
        return ret;
    }
}

Or better...

class ResponseItem {
    public ResponseItemVO toVO(){
        ResponseItemVO ret = new ResponseItemVO();
        ret.setErrorDetails(getErrorDetails());
        ret.setResult(getResult().toVO());
        ret.setIdentifier(getIdentifier().toVO());
        return ret;
    }
}