使用JDK8编译Java应用程序会导致CPU负载更高

8

我有一个Java应用程序,在Windows的Eclipse Luna中开发,在Amazon EC2(c3.large,Amazon Linux)上运行。该应用程序以非常一致的速率处理工作。当我使用JDK 8u31构建应用程序时,EC2的CPU负载比相同应用程序使用JDK 7u75构建时更高。

该应用程序最初在EC2上运行默认的JRE,并添加了OpenJDK 1.8.0.31,以利用Java 8 Process waitFor(long timeout,TimeUnit unit)。此应用程序主要工作涉及调用Runtime.exec来使用应用程序。

$ sudo alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*  1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
 + 2           /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.31-2.b13.5.amzn1.x86_64/jre/bin/java

当应用程序构建版本为1.7时,负载平均值的示例:

top - 00:20:28 up 4 days, 10:41,  4 users,  load average: 0.37, 0.26, 0.52

当应用程序构建在1.8上时,示例负载平均值:

top - 23:45:52 up 4 days, 10:06,  4 users,  load average: 2.28, 2.60, 2.01

看起来可能与Open JDK 1.8.0.31有关,但我不知道如何调试。在Eclipse Luna中,我只是在1.7和1.8之间更改了兼容级别和构建,没有进行任何代码更改。为什么负载差异如此之大呢?

更新:

当我在EC2上使用Oracle JDK时,也发现类似的高CPU负载。

$ sudo alternatives --config java

There are 3 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
   1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.31-2.b13.5.amzn1.x86_64/jre/bin/java
*+ 3           /usr/java/jdk1.8.0_31/bin/java

负载均衡:

top - 01:45:27 up 4 days, 12:06,  4 users,  load average: 2.28, 1.50, 1.04

你能在自己的机器上重现这个问题吗? - fge
我在多个EC2实例上看到过这个问题(包括OpenJDK)。我无法在本地机器上生成工作负载,但我会研究模拟工作负载的方法。 - DanielB6
为了区分编译器和JRE的影响:如果您在兼容性1.7下编译并在1.8 JRE上运行会发生什么? - Stephan Herrmann
1
这两个版本有区别吗?也就是说,你是否为两者编译了完全相同的源代码?你提到添加了“OpenJDK 1.8.0.31以利用Java 8进程waitFor(long timeout, TimeUnit unit)” ,所以如果你实现了这个更改,可能会出现基于代码的问题。 - Others
我尝试了两种主要情况 - 1)完全相同的源代码,JAR文件分别构建为1.7和1.8。2)源代码除了process.waitFor()更改为process.waitFor(30,TimeUnit.SECONDS)之外都相同。在这两种情况下,使用1.8时负载平均值(和CPU利用率)要高得多。 - DanielB6
2
那似乎排除了由于使用不同API引起的差异。但我们仍然不知道,是否在1.8上运行为1.7构建的jar文件会显示相同的效果(请参见我的先前评论)。这个实验将清楚地表明问题是需要在编译器级别还是在JVM级别进行分析。 - Stephan Herrmann
2个回答

4
您已经描述了症状,但我认为这还不足以进行操作,因为在任何地方(Google、Java错误档案等)搜索“Java 8高CPU”之类的内容并找到有用的结果将是不可能的。不幸的是,您需要收集更多有关使用CPU的信息。以下是一些如何做到这一点的想法:
  1. 使用VisualVM等工具进行分析。负载差异极大,因此您可能只能看到使用CPU的内容。
  2. 查找繁忙的线程。您可以拍摄一堆线程快照并进行目测,或尝试使用jvmtop等工具。
  3. 检查垃圾回收器正在执行的操作,可以通过启用GC日志记录或使用jstat等工具来实现。
  4. 使用strace跟踪系统调用的执行情况。

谢谢提供这些信息,我会查看这些工具并在收集到任何结果后回复。 - DanielB6

1

我认为在使用process.waitFor(30, TimeUnit.SECONDS)的情况下可能是可行的。但这并没有说明在1.7和1.8中都使用相同的process.waitFor(),且没有代码更改的情况。 - DanielB6
1
没错,我在注释中漏掉了那个。也许1.8中的waitFor()实现有缺陷,会调用带有超时的版本?这听起来像是一个繁琐的缩减到最小示例的情况... - Gerhard Wesp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接