转载

OpenJ9 和 HotSpot 的对比 Part 1

OpenJ9 和  IBM J9 是来自默认 Oracle HotSpot JVM 的不同 JVM 实现。使用现代的  adoptopenjdk 预置 Docker 镜像,你可以轻易地切换和测试不同的组合,并且可以为你选择合适的 JVM。

这个传言看起来是真的,OpenJ9 在内存使用方面看起来已经领先 HotSpot 了。HotSpot 似乎在 CPU 方面具有优势。

OpenJ9

在 Java 的世界中,大多人都熟悉 OpenJDK。这是一个完全的 JDK 实现,包括对 HotSpot JVM 引擎的实现。不是很多开发者了解或尝试选择 HotSpot。询问周围的同事后,他们都记得 JRockit 这个名字,但没有人提起 IBM J9 及(或) Eclipse OpenJ9。

我已经了解到了 OpenJ9 擅长于内存管理 ,而且在云/容器中的使用上已经经过了精简。OpenJ9 是一个独立的对 JVM 的实现。它源于 IBM 的 Java SDK/IBM J9,它的历史能追溯到 OTI Technologies Envy Smalltalk(感谢 Dan Heidinga!)。

随着微服务使用率的提升(而且 Java 中的大多数服务都 不是 特别小)。我认为它将会再次变成一个热门话题!

测试

在 Docker 时代之前,比较不同的 JVM 版本是相对困难的。你需要下载、安装、编写脚本和运行所有相关项。但现在很多提前制作好的镜像可以通过在线获得。

这里是我关于如何测试 JVM 的想法:

  1. 创建一个简单的 Spring Boot 应用程序

  2. 在各种 Docker 镜像中启动该应用程序

  3. 在启动和 GC 后测量内存使用情况

  4. 测量运行小型 CPU 密集型测试所需的时间

这绝不是一个彻底的测试或基准测试,但它应该能给我们一个我们可以从虚拟机中获得什么的大致想法。

Spring Boot 应用

我创建的 Spring Boot 应用包含了下面的端点:

  1. 一个 REST 端点调用 GC( 尽量让它合理)

  2. 一个 REST 端点创建了 1000 个大 型随机数组并对其排序,返回运行时长(单位为 ms)

下面列出了 CPU 测试:

@RestController
public class LoadTestController {

    @RequestMapping("/loadtest")
    public LoadTestResult loadtest() {

        long before = System.currentTimeMillis();

        Random random = new Random();

        for(int i = 0; i < 1000; i++) {
            long[] data = new long[1000000];
            for(int l = 0; l < data.length; l++) {
                data[l] = random.nextLong();
            }
            Arrays.sort(data);
        }

        return new LoadTestResult(System.currentTimeMillis() - before);
    }
}

就这个测试是否合理和切题…我们可以无休止地争论,但是对于可以期待它是什么样的表现形式,它能给我们一些基本概念。如果传言中的内存提升是真的,可能会出现性能干扰吗?是否存在性能折中的情况?

JVM 镜像

我决定对以下镜像进行测试。

首先我们有 8/9/10/11 版本的(轻量级) openjdk 镜像:

  • openjdk:8-slim

  • openjdk:9-slim

  • openjdk:10-slim

  • openjdk:11-slim

接下来是 8/9/10 版本的 adoptopenjdk 镜像:

  • adoptopenjdk/openjdk8

  • adoptopenjdk/openjdk9

  • adoptopenjdk/openjdk10

之后是 OpenJ9,也是由 adoptopenjdk 为 8/9 版本所提供的,同时包含为 9 发布的每日构建版(相关内容请查看我之前的博文):

  • adoptopenjdk/openjdk8-openj9

  • adoptopenjdk/openjdk9-openj9

  • adoptopenjdk/openjdk9-openj9:nightly

同时我决定也引入 IBM 自家的 J9 镜像:

  • ibmcom/ibmjava:8-jre

使用 Docker 进行测试

在构建我的 Spring Boot 应用之后,我使用下面命令启动了每个 Docker 镜像:

docker run -it -v /Projects/temp/spring-boot-example:/app/spring-boot-example -p 8080:8080 IMAGE_NAME /bin/bash

我将 "spring-boot-example" 项目目录映射到了 "/apps/spring-boot-example",然后我就可以启动容器中的 JAR 文件。同时我将端口 8080 转发到我的主机,这样我就可以调用这些端点。

下一步,在容器内部,我启动了 Spring Boot 应用:

java -jar /app/spring-boot-example/target/spring-boot-example-0.0.1-SNAPSHOT.jar

等待一段时间,调用几次端点,并执行一个 GC,之后我测量内存使用情况。

然后,我调用了包含数组排序测试的 "/loadtest" 端点,然后等待结果.

内存基准测试

下面是示例的 Spring Boot 应用的内存使用结果:

OpenJ9 和 HotSpot 的对比 Part 1

首先你可以看到 Java 8 的内存使用明显高于 Java 9 和 10。

但是令人最吃惊的是 OpenJ9 和 J9 使用的内存减少的量,如果将 Java8 和 OpenJ9 对比,后者几乎是前者的 4 倍。我很惊奇,这是怎么形成的?现在我们几乎可以将 Spring Boot 服务称为微服务 (micro) 了!

我也尝试过运行一些生产环境中的 Spring Boot 代码(不仅仅是简单的示例),通过它们我看到了内存使用量减少了 40-50% 的改进。

CPU 基准测试

我已经了解到,如果您从 CPU 密集任务方面去对比,OpenJ9 不如 HotSpot。这就是为什么我为此创建了一个小测试。

给具有 1000000 个随机 long 值的 1000 个数组排序。这大约需要花费 100 秒,这应该给了 JVM 足够的时间去调整和优化。我已经为每个测试镜像调用了两次基准测试。我记录了第二次的时间以尝试去排除热启动的时间。

OpenJ9 和 HotSpot 的对比 Part 1

从图表中我们能看到 J9 和 OpenJ9 的镜像确实很慢,最快不超过 18%。从这个特定测试用例看来,Java 8 击败了绝大多数 Java 9 的实现(除了 OpenJ9 以外)。

下一步是什么?

在生产环境中,与内存问题相比,我当前的项目存在更多的 CPU 问题(频繁运行过程中的内存溢出,而 CPU 使用率是 1-2%)。在不久的将来,我们肯定会切换至使用 OpenJ9。

测试期间,我们也确实遇到了一些问题:

  1. Hessian:(二进制协议)有一个内置的假设,即 System.identityHashCode 总是返回一个正数。对于 HotSpot 来说这是个事实,但是 OpenJ9/J9 也可以返回负数。这是个公开的问题,Hessian 项目在近几年内并没有解决这个问题,似乎它已经停止维护了?所以我们的解决方案是远离 Hessian。

  2. Instana:我们热爱我们的监控工具 Instana,但是它连接代理到 OpenJ9/J9 的时候出了些问题。幸运的是,Instana 的开发者帮助我们确认了一个错误,还进行了修复(并且会自动更新,哇!)

我没有注意到的开放性问题:

  1. 你仍然可以在 OpenJ9 中对信息进行 get/use jmap/hprof 等操作吗?

  2. 它将如何在更长久的生产环境中保持运行?

  3. 我们会发现其他的奇怪错误吗?感觉很棘手…

你尝试过使用 OpenJ9/J9 吗?在评论区中和大家分享你的看法吧!

原文  https://www.oschina.net/translate/openj9-jvm-shootout
正文到此结束
Loading...