解决编译缓慢问题
Troubleshoot slow compilation
我应该如何调查和解决编译速度慢的问题?
我的项目有大约 100 个 classes,编译时间超过 45 秒,这对我来说似乎很慢。作为参考,我有另一个项目有 50 个 classes,编译时间为 3 秒。
ps:
- 我使用 maven 作为构建工具。 maven 编译需要 ~50 秒(
mvn clean compile
),其中 45 秒用于 运行 javac(由 运行 -X
选项确认)。
- 增加内存量没有帮助(
-Xms500m
)
- 我可以提供更多关于我的项目的信息,但它是相当标准的,所以我不确定哪些信息是相关的。
更新
多亏了 Tagir 的点子,我终于找到了罪魁祸首之一。 class 编译时间增加 20 秒:
import org.jooq.DSLContext;
import org.jooq.Field;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.round;
import static org.jooq.impl.DSL.sum;
class Test {
static Object fast(DSLContext sql) {
Field<Double> a = field("a").cast(Double.class);
return sql.select()
.having(round(sum(a).cast(Double.class), 2).ne(0d));
}
static Object slow(DSLContext sql) {
return sql.select()
.having(round(sum(field("a").cast(Double.class)).cast(Double.class), 2).ne(0d));
}
}
如果slow
方法被注释掉,编译时间恢复正常
Java 8 的一个不太为人所知的功能是 Generalized Target-Type Inference。
虽然它允许编写更清晰的代码,但这需要为 Javac
做更多的工作。有时这会导致类型推断问题的指数复杂度。这是一个已知问题,但遗憾的是仍未解决 - 请参阅 JDK-8055984, JDK-8067767.
解决方法是在 Java 7 兼容级别编译:javac -source 7
,或者只是使用更简单的结构。
疑难解答 - 一般方法
您可以首先重新创建一个空项目,然后将包一个一个地添加回来,直到编译时间受到影响 - 这应该可以帮助您识别导致问题的包。
然后您可以删除包中的所有 类,然后将它们一一添加回来 - 这应该可以帮助您找到导致问题的 类。
然后您可以从每个 类 中删除所有方法,然后将它们一个接一个地添加回来,直到您看到编译时间增加(您可以节省时间 only recompiling that one class)。
具体原因
在这种情况下,根本原因似乎是 javac 中的一个错误,所以我已经提交了一个 bug report which has been marked as a duplicate of "JEP 215: Tiered Attribution for javac",目标是在 Java 9.
与此同时,解决方法是在存在使用泛型类型推断的嵌套泛型方法调用时引入局部变量,但不幸的是,这并不总是有效...
有多个与此相关的讨论。添加这些引用以防其他人偶然发现这个 post.
初步讨论:
- https://groups.google.com/forum/#!topic/jooq-user/grv6Wu_sFtA
- Slow compilation with jOOQ 3.6+, plain SQL, and the javac compiler
就我而言,我基本上尝试尽可能多地使用自动生成的代码。或者,您可以尝试 Lukas 在上面链接的 Whosebug post 中提到的建议。
我应该如何调查和解决编译速度慢的问题?
我的项目有大约 100 个 classes,编译时间超过 45 秒,这对我来说似乎很慢。作为参考,我有另一个项目有 50 个 classes,编译时间为 3 秒。
ps:
- 我使用 maven 作为构建工具。 maven 编译需要 ~50 秒(
mvn clean compile
),其中 45 秒用于 运行 javac(由 运行-X
选项确认)。 - 增加内存量没有帮助(
-Xms500m
) - 我可以提供更多关于我的项目的信息,但它是相当标准的,所以我不确定哪些信息是相关的。
更新
多亏了 Tagir 的点子,我终于找到了罪魁祸首之一。 class 编译时间增加 20 秒:
import org.jooq.DSLContext;
import org.jooq.Field;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.round;
import static org.jooq.impl.DSL.sum;
class Test {
static Object fast(DSLContext sql) {
Field<Double> a = field("a").cast(Double.class);
return sql.select()
.having(round(sum(a).cast(Double.class), 2).ne(0d));
}
static Object slow(DSLContext sql) {
return sql.select()
.having(round(sum(field("a").cast(Double.class)).cast(Double.class), 2).ne(0d));
}
}
如果slow
方法被注释掉,编译时间恢复正常
Java 8 的一个不太为人所知的功能是 Generalized Target-Type Inference。
虽然它允许编写更清晰的代码,但这需要为 Javac
做更多的工作。有时这会导致类型推断问题的指数复杂度。这是一个已知问题,但遗憾的是仍未解决 - 请参阅 JDK-8055984, JDK-8067767.
解决方法是在 Java 7 兼容级别编译:javac -source 7
,或者只是使用更简单的结构。
疑难解答 - 一般方法
您可以首先重新创建一个空项目,然后将包一个一个地添加回来,直到编译时间受到影响 - 这应该可以帮助您识别导致问题的包。
然后您可以删除包中的所有 类,然后将它们一一添加回来 - 这应该可以帮助您找到导致问题的 类。
然后您可以从每个 类 中删除所有方法,然后将它们一个接一个地添加回来,直到您看到编译时间增加(您可以节省时间 only recompiling that one class)。
具体原因
在这种情况下,根本原因似乎是 javac 中的一个错误,所以我已经提交了一个 bug report which has been marked as a duplicate of "JEP 215: Tiered Attribution for javac",目标是在 Java 9.
与此同时,解决方法是在存在使用泛型类型推断的嵌套泛型方法调用时引入局部变量,但不幸的是,这并不总是有效...
有多个与此相关的讨论。添加这些引用以防其他人偶然发现这个 post.
初步讨论:
- https://groups.google.com/forum/#!topic/jooq-user/grv6Wu_sFtA
- Slow compilation with jOOQ 3.6+, plain SQL, and the javac compiler
就我而言,我基本上尝试尽可能多地使用自动生成的代码。或者,您可以尝试 Lukas 在上面链接的 Whosebug post 中提到的建议。