jdk17新特性

前言

JDK17于2021年9月14日正式发布,这是继JDK11发布3年以来的又一个长支持(LTS)的java版本,其带来了14项JEP(JDK增强建议)更新,包括10个新功能以及2个功能的删除和2个功能的弃用。
本文将针对部分重点功能做介绍,其余没有说到的新功能请自行百度。

spring官方已经宣布,Spring Framework 6和Spring Boot 3将在2022年第四季度达到生产可用的发布标准,且运行时至少需要JDK17环境,因此我们为了以后技术的更新,需要了解和学习JDK17。

正文

根据官方介绍,JDK17共有14个JEP:

  • 306:Restore Always-Strict Floating-PointSemantics / 恢复始终严格的浮点语义
  • 356:Enhanced Pseudo-Random Number Generators / 增强型伪随机数发生器
  • 382:New macOS Rendering Pipeline / 新的 macOS 渲染管道
  • 391:macOS/AArch64 Port / macOS/AArch64 平台支持
  • 398:Deprecate the Applet API for Removal / 弃用即将删除的Applet API
  • 403:Strongly Encapsulate JDK Internals / 强封装JDK的内部API
  • 406:Pattern Matching for switch(Preview) / Switch模式匹配(预览)
  • 407:Remove RMI Activation / 删除 RMI 激活机制
  • 409:Sealed Classes / 密封类
  • 410:Remove the Experimental AOT and JIT Compiler / 删除实验性 AOT 和 JIT 编译器
  • 411:Deprecate the Security Manager For Removal / 弃用即将删除的安全管理器
  • 412:Foreign Function & Memory API(Incubator) / 外部函数和内存 API(孵化器)
  • 414:Vector API (Second Incubator) / 矢量 API(二次孵化)
  • 415:Context-Specific Deserialization Filters / 特定于上下文的反序列化过滤器

其中 407410 为删除,398411 为弃用,其余都为新功能。

306:恢复始终严格的浮点语义

介绍这个更新之前,需要了解一些背景:

IEEE(电气和电子工程协会)为浮点计算和以各种格式存储浮点值制定了一种标准,包括单精度(32位,用于java)float、双精度(64位,用于java)double,另外一些硬件还提供扩展精度格式,
以提供更高的精度和更大的指数范围。在这样的架构下,使用扩展格式计算中间结果可能更有效,还能避免可能发生的舍入错误、上溢和下溢,但是会导致程序在此类架构上产生不同的输出,且x87浮点架构在x86机器上使用扩展精度代价很昂贵。

在JVM1.2之前浮点计算是严格要求的,也就是说浮点值的计算结果都必须表现的和标准一样,这使得在常见的x87浮点指令集平台上在需要的地方发生溢出的代价变得昂贵。
JVM1.2开始,默认情况下允许中间计算超出IEEE32位和64位格式相关标准指数范围,这样一来在x87平台上,溢出和下溢可能不会在预期的地方发生,而是产生重复性更低更具有意义的结果。

在没有上溢和下溢的情况下,如果需要得到重复的结果,java提供了一个当前已过时且未使用的关键字 strictfp ,strictfp修饰符确保浮点计算在所有平台发生上溢和下溢的地方相同,且中间值表示为IEEE单精度和双精度值。

该关键字可用于类、接口、非抽象方法。在方法上添加时内部所有计算都使用严格的浮点数学计算;在类上添加时类中所有计算都使用严格的浮点数学计算

例如:

public strictfp class SFPClass {
    // ......
}
public class testClass {
    public double strictfp calculate() {
        // ......
    }
}

由于当下支持SSE2指令集的x86处理器不再需要x87浮点指令集,因此JDK17再次严格要求所有浮点计算,恢复了1.2之前的语义。

  • 356:增强型伪随机数发生器

JDK17之前的随机数API没有统一的接口,要实现自己的随机算法页比较麻烦。

JDK7为伪随机数生成器(PRNG)提供新的接口类型和实现,使程序使用各种PRNG算法更加容易,更好的支持流式编程。
提供了一个新的接口 RandomGenerator ,为所有PRNG算法提供统一的API,同时提供了一个新的类 RandomGeneratorFactory 来构造各种 RandomGenerator 实例。

例:

随机数生成器统一由 RandomGeneratorFactory 生成

public static void main(String[] args) {
    //生成10个10以内的随机数
    RandomGeneratorFactory<RandomGenerator> L128X1024MixRandom = RandomGeneratorFactory.of("L128X1024MixRandom");
    RandomGenerator randomGenerator = L128X1024MixRandom.create(System.currentTimeMillis());
    for (int i = 0; i < 10; i++) {
        System.out.println(randomGenerator.nextInt(10));
    }
}
7
8
0
7
4
0
6
0
8
1

通过 RandomGeneratorFactory.of(“随机数生成算法”) 方法获得生成器

RandomGeneratorFactory.of("L32X64MixRandom")

RandomGeneratorFactory.all() 方法可以获得所有随机数生成算法,输出所有随机算法名称:

public static void main(String[] args) {
    RandomGeneratorFactory.all().forEach(e -> System.out.println(e.group() + "-" + e.name()));
}
LXM-L32X64MixRandom
LXM-L128X128MixRandom
LXM-L64X128MixRandom
Legacy-SecureRandom
LXM-L128X1024MixRandom
LXM-L64X128StarStarRandom
Xoshiro-Xoshiro256PlusPlus
LXM-L64X256MixRandom
Legacy-Random
Xoroshiro-Xoroshiro128PlusPlus
LXM-L128X256MixRandom
Legacy-SplittableRandom
LXM-L64X1024MixRandom

结果可以看到原来的 Random 算法也支持。

382 新的 macOS 渲染管道,391 macOS/AArch64 平台支持

都是针对MacOS做的优化更新,不做过多介绍。

382是使用Apple Metal替代OpenGL渲染库,因为Apple在MacOS 10.14中弃用了OpenGL取而代之的是Metal框架。

391是将JDK移植到新架构的macOS/AArch64。

398 弃用即将删除的Applet API

Applet API实际上是早就过时的,因为所有浏览器都已删除或计划删除java浏览器插件,API早在JDK9就已经被标记为启用,JDK17正式删除。

403 强封装JDK的内部API

简而言之就是java内部的大部分类,除了关键的内部API,如sun.misc.Unsafe类,都进行强封装,默认情况下不允许开发人员利用反射等手段去访问内部非public的类、成员变量等,使java更加安全。
但是可以通过设置参数–add-export或–add-opens来指定哪些类可以被访问。

406 Switch模式匹配(预览)

JDK17为switch支持模式匹配,当前还只是预览版,可能在后续JDK版本中会扶正,也可能删除。

switch在JDK12和switch13中连续得到了语法加强,此次更新更是完善了switch的使用,使其更加美观和简洁。

JDK11以前:

public static void JDK11_switch() {
    String day = "MONDAY";
    switch (day) {
        case "MONDAY":
            System.out.println(1);
            break;
        case "TUESDAY":
            System.out.println(2);
            break;
        case "WEDNESDAY":
            System.out.println(3);
            break;
        case "THURSDAY":
            System.out.println(4);
            break;
        case "FRIDAY":
            System.out.println(5);
            break;
        case "SATURDAY":
            System.out.println(6);
            break;
        case "SUNDAY":
            System.out.println(7);
            break;
        default:
            System.out.println(0);
            break;
    }
}

JDK12简化了写法:

public static void JDK12_switch() {
    String day = "MONDAY";
    switch (day) {
        case "MONDAY" -> System.out.println(1);
        case "TUESDAY" -> System.out.println(2);
        case "WEDNESDAY" -> System.out.println(3);
        case "THURSDAY" -> System.out.println(4);
        case "FRIDAY" -> System.out.println(5);
        case "SATURDAY" -> System.out.println(6);
        case "SUNDAY" -> System.out.println(7);
        default -> System.out.println(0);
    }
}

JDK13在简化的基础上增加了返回值:

public static void JDK13_switch() {
    String day = "MONDAY";
    int i = switch (day) {
        case "MONDAY" -> 1;
        case "TUESDAY" -> 2;
        case "WEDNESDAY" -> 3;
        case "THURSDAY" -> 4;
        case "FRIDAY" -> 5;
        case "SATURDAY" -> 6;
        case "SUNDAY" -> 7;
        default -> 0;
    };
    System.out.println(i);
}

从JDK17以前,switch不支持instanceof,如果有多个instanceof只能用if-else来表达:

public static void JDK17_before_instanceof_switch(Object o) {
    //o instanceof Integer i 为JDK16新特性
    if (o instanceof Integer i) {
        System.out.println(i);
    } else if (o instanceof Long l) {
        System.out.println(l);
    } else if (o instanceof Double d) {
        System.out.println(d);
    } else if (o instanceof String s) {
        System.out.println(s);
    } else {
        System.out.println("UNKNOWN");
    }
}

JDK17之后,switch开始支持instanceof,简化写法:

public static void JDK17_instanceof_switch(Object o) {
    switch (o) {
        case Integer i -> System.out.println(i);
        case Long l -> System.out.println(l);
        case Double d -> System.out.println(d);
        case String s -> System.out.println(s);
        default -> System.out.println("UNKNOWN");
    }
}
407 删除 RMI 激活机制

RMI(远程方法调用)是实现RPC(远程过程调用)的java api。由于web技术的发展,有关过滤请求、身份认证、安全性等问题都已经在web服务领域得到解决,但这些机制都不在RMI的激活模型中,
即便是不要激活机制也不影响RMI的其他部分,还能减少维护成本,所以在JDK15中把RMI的激活机制标记为弃用,JDK17中正式删除。

409 密封类

密封类(Sealed Classes)是JDK15引入的,在JDK17正式转正。
密封类可以对继承或者实现他们的类进行限制。

例如:

Person类被 sealed 修饰,只允许(permits)Male和Female类继承,继承的类必须有 final 或者 no-sealed 来修饰。

Function接口被 sealed 修饰,只允许(permits)Male和Female类实现,实现的类必须有 final 或者 no-sealed 来修饰。

public static void main(String[] args) {
    Male male = new Male();
    male.eat();

    Female female = new Female();
    female.eat();
}

static sealed class Person permits Male, Female {
}

static final class Male extends Person implements Function {
    @Override
    public void eat() {
        System.out.println("eat 1");
    }
}

static non-sealed class Female extends Person implements Function {
    @Override
    public void eat() {
        System.out.println("eat 2");
    }
}

sealed interface Function permits Male, Female {
    void eat();
}
410 删除实验性 AOT 和 JIT 编译器

AOT(Ahead Of Time 运行前编译)即jaotc,可以将java代码编译成二进制,JVM直接用这些二进制,而不是在运行时再花时间用JIT(Just in Time 即时编译)编译。

AOT优点在于占用内存低,启动速度快,缺点是在程序运行前编译会使程序的安装时间增加,牺牲java的一致性;JIT优点在于吞吐量高,可以根据当前情况实时编译生成最优机器指令,
缺点是编译需要占用运行时资源,启动速度较慢。

这是oracle自己做的实验性质的东西,由于使用实在太少,并且第三方有成熟的虚拟机(官方推荐 GraalVM ),所以就删除了。

  • ####411 弃用即将删除的安全管理器

SecurityManager 安全管理器是一个允许应用程序实现安全策略的类,但是这么多年很少被使用,为了减少维护工作,在JDK17中标记为弃用,将来会和Applet API一起删除。

412 外部函数和内存 API(孵化器)

这个新功能与JNI(java本地接口)有关,JNI允许java程序与程序以外的代码或数据做交互,常见的JNI即Thread类里的start()方法。JNI只能和以C和C++语言编写的库进行交互,
在通用性上有所不足,且JNI无法监控JVM以外的代码运行情况,外部可以通过getStaticField等函数访问JDk内部,甚至改变在final修饰下的字段值,本质上JNI是不安全的。
因此java开发人员觉得有一个更加安全易用的,基于java模型的JNI替代API,就有了这个孵化器,说不定在后面哪个版本的JDK中就孵化成功了。

414 矢量 API(二次孵化)

这是一个矢量API,用于矢量计算,日常开发中不会用到。

415 特定于上下文的反序列化过滤器

这个新特性是为了反序列化的安全性考虑的,由于反序列化的数据内容决定了创建的对象、字段值以及他们之间的引用关系。多数情况下了流中的字节是从未知、不受信任或未经身份验证的客户端接收的,
因此可能会有利用反序列化攻击程序的情况发生,为了避免这类问题JDK9新增了反序列化过滤器,JDK17在此基础上又新增了基于特定上下文的反序列化过滤器,可以通过JVM范围的过滤器工厂配置特定于上下文和动态选择的反序列化过滤器。

有史以来最快的JAVA

JDK17号称有史以来最快的java,现在实际测试一下运行速度:

分别采用oracle JDK8,open JDK11,open JDK17做测试,对比一下差距。

测试环境硬件条件:Mac Book;CPU Intel Core i7 6核12线程;32G内存

java version "1.8.0_301"
Java(TM) SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
openjdk version "17.0.1" 2021-10-19
OpenJDK Runtime Environment (build 17.0.1+12-39)
OpenJDK 64-Bit Server VM (build 17.0.1+12-39, mixed mode, sharing)

1、循环输出10000000(一千万)次

 public static void main(String[] args) {
     long begin = System.currentTimeMillis();
     //循环一千万次
     for (int i = 0; i < 10000000; i++) {
         System.out.println("第" + i + "次循环");
     }
     long end = System.currentTimeMillis();
     System.out.println("共耗时:" + (end - begin) + "毫秒");
 }

JDK8:

...
第9999996次循环
第9999997次循环
第9999998次循环
第9999999次循环
共耗时:28593毫秒

JDK11:

...
第9999996次循环
第9999997次循环
第9999998次循环
第9999999次循环
共耗时:25196毫秒

JDK17:

...
第9999996次循环
第9999997次循环
第9999998次循环
第9999999次循环
共耗时:10722毫秒

不同版本对比:

JDk版本耗时(毫秒)提升(相较于参与测试的上一版本)
1.828593-
112519611%+
171072257%+

可以看到在循环输出一千万次测试中,JDK11比JDK8时间缩短了11%+,JDK17比JDK11时间缩短了57%+。

2、反射的方式给简单Bean的字段set值100000000(一亿)次

public class TestBean {
    private String name;
    private Integer age;
    ...
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    TestBean bean = new TestBean();
    long begin = System.currentTimeMillis();
    //循环100000000次
    for (int i = 0; i < 100000000; i++) {
        Class clazz = Class.forName("com.xia.java17.bean.TestBean");
        Method method = clazz.getMethod("setAge", Integer.class);
        method.invoke(bean,18);
    }
    long end = System.currentTimeMillis();
    System.out.println("共耗时:" + (end - begin) + "毫秒");
}

JDK8:

共耗时:90207毫秒

JDK11:

共耗时:67667毫秒

JDK17:

共耗时:37551毫秒

不同版本对比:

JDk版本耗时(毫秒)提升(相较于参与测试的上一版本)
1.890207-
116766725%-
173755145%-

可以看到在反射循环一亿次set值测试中,JDK11比JDK8时间缩短了接近25%,JDK17比JDK11时间缩短了接近45%。

3、GC效率

public class TestBean {
    private String name;
    private Integer age;
    ...
}
public static void main(String[] args) {
        List<TestBean> beans = new ArrayList<>();
        //循环10000000次
        for (int i = 0; i < 10000000; i++) {
            TestBean bean = new TestBean("测试", 18);
            beans.add(bean);
        }
    }

VM参数设置:-Xms1024m -Xmx1024m -XX:+PrintGC/-Xlog:gc -XX:+UseG1GC

使用默认的G1(低延迟垃圾回收器),这里只选取有用的日志内容

-Xms1024m 起始堆内存大小1024m
-Xmx1024m 最大堆内存大小1024m
-XX:+PrintGC/-Xlog:gc 输出GC日志 JDK11后-XX:+PrintGC参数弃用,建议使用-Xlog:gc
-XX:+UseG1GC 使用G1垃圾回收器

JDK8:

[GC pause (G1 Evacuation Pause) (young) 1024K->440K(1024M), 0.0015700 secs]
[GC pause (G1 Evacuation Pause) (young) 1464K->1026K(1024M), 0.0025434 secs]
[GC pause (G1 Evacuation Pause) (young) 2050K->1850K(1024M), 0.0027888 secs]
[GC pause (G1 Evacuation Pause) (young) 2874K->2970K(1024M), 0.0028041 secs]
[GC pause (G1 Evacuation Pause) (young) 3994K->3707K(1024M), 0.0018124 secs]
[GC pause (G1 Evacuation Pause) (young) 5356K->5142K(1024M), 0.0038780 secs]
[GC pause (G1 Evacuation Pause) (young) 7104K->7540K(1024M), 0.0050190 secs]
[GC pause (G1 Evacuation Pause) (young) 8564K->9203K(1024M), 0.0050022 secs]
[GC pause (G1 Evacuation Pause) (young) 11M->11M(1024M), 0.0054110 secs]
[GC pause (G1 Evacuation Pause) (young) 12M->13M(1024M), 0.0048150 secs]
......
[GC pause (G1 Evacuation Pause) (young) 435M->437M(1024M), 0.0072393 secs]
[GC pause (G1 Evacuation Pause) (young) 494M->494M(1024M), 0.0302716 secs]
[GC pause (G1 Evacuation Pause) (young) 496M->497M(1024M), 0.0059715 secs]
[GC pause (G1 Evacuation Pause) (young) 499M->500M(1024M), 0.0069864 secs]
[GC pause (G1 Evacuation Pause) (young) 502M->504M(1024M), 0.0062188 secs]
[GC pause (G1 Evacuation Pause) (young) 506M->506M(1024M), 0.0058288 secs]
[GC pause (G1 Evacuation Pause) (young) 508M->509M(1024M), 0.0059459 secs]
[GC pause (G1 Evacuation Pause) (young) 511M->512M(1024M), 0.0070257 secs]
[GC pause (G1 Evacuation Pause) (young) 514M->515M(1024M), 0.0055958 secs]

总耗时 864.5835 毫秒。

平均一次GC时间为 7.145318毫秒

JDK11:

[0.115s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 1M->0M(1024M) 1.342ms
[0.152s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 1M->1M(1024M) 1.348ms
[0.156s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 2M->1M(1024M) 1.937ms
[0.160s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 2M->2M(1024M) 1.978ms
[0.163s][info][gc] GC(4) Pause Young (Normal) (G1 Evacuation Pause) 3M->3M(1024M) 1.571ms
[0.168s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 5M->5M(1024M) 3.936ms
[0.175s][info][gc] GC(6) Pause Young (Normal) (G1 Evacuation Pause) 7M->7M(1024M) 5.235ms
[0.180s][info][gc] GC(7) Pause Young (Normal) (G1 Evacuation Pause) 8M->8M(1024M) 4.173ms
[0.184s][info][gc] GC(8) Pause Young (Normal) (G1 Evacuation Pause) 11M->11M(1024M) 3.195ms
[0.189s][info][gc] GC(9) Pause Young (Normal) (G1 Evacuation Pause) 12M->12M(1024M) 3.616ms
......
[1.345s][info][gc] GC(229) Pause Young (Normal) (G1 Evacuation Pause) 405M->405M(1024M) 4.301ms
[1.350s][info][gc] GC(230) Pause Young (Normal) (G1 Evacuation Pause) 406M->406M(1024M) 3.782ms
[1.356s][info][gc] GC(231) Pause Young (Normal) (G1 Evacuation Pause) 407M->407M(1024M) 5.548ms
[1.362s][info][gc] GC(232) Pause Young (Normal) (G1 Evacuation Pause) 408M->408M(1024M) 5.292ms
[1.367s][info][gc] GC(233) Pause Young (Normal) (G1 Evacuation Pause) 409M->409M(1024M) 3.786ms
[1.372s][info][gc] GC(234) Pause Young (Normal) (G1 Evacuation Pause) 410M->411M(1024M) 4.766ms
[1.377s][info][gc] GC(235) Pause Young (Normal) (G1 Evacuation Pause) 412M->412M(1024M) 4.031ms
[1.382s][info][gc] GC(236) Pause Young (Normal) (G1 Evacuation Pause) 413M->413M(1024M) 4.430ms
[1.388s][info][gc] GC(237) Pause Young (Normal) (G1 Evacuation Pause) 414M->414M(1024M) 5.147ms
[1.393s][info][gc] GC(238) Pause Young (Normal) (G1 Evacuation Pause) 415M->415M(1024M) 4.637ms

总耗时 989.583毫秒

平均一次GC时间为 4.140514644毫秒

JDK17:

[0.106s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 1M->1M(1024M) 1.591ms
[0.111s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 2M->2M(1024M) 1.644ms
[0.115s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 2M->2M(1024M) 2.039ms
[0.118s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 3M->3M(1024M) 1.325ms
[0.120s][info][gc] GC(4) Pause Young (Normal) (G1 Evacuation Pause) 4M->4M(1024M) 1.452ms
[0.126s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 6M->5M(1024M) 3.681ms
[0.132s][info][gc] GC(6) Pause Young (Normal) (G1 Evacuation Pause) 7M->7M(1024M) 3.927ms
[0.136s][info][gc] GC(7) Pause Young (Normal) (G1 Evacuation Pause) 8M->8M(1024M) 3.406ms
[0.142s][info][gc] GC(8) Pause Young (Normal) (G1 Evacuation Pause) 11M->11M(1024M) 3.917ms
[0.147s][info][gc] GC(9) Pause Young (Normal) (G1 Evacuation Pause) 12M->12M(1024M) 4.013ms
......
[1.142s][info][gc] GC(228) Pause Young (Normal) (G1 Evacuation Pause) 405M->405M(1024M) 5.909ms
[1.149s][info][gc] GC(229) Pause Young (Normal) (G1 Evacuation Pause) 406M->407M(1024M) 5.323ms
[1.154s][info][gc] GC(230) Pause Young (Normal) (G1 Evacuation Pause) 408M->408M(1024M) 4.636ms
[1.161s][info][gc] GC(231) Pause Young (Normal) (G1 Evacuation Pause) 409M->409M(1024M) 5.259ms
[1.165s][info][gc] GC(232) Pause Young (Normal) (G1 Evacuation Pause) 410M->410M(1024M) 3.431ms
[1.170s][info][gc] GC(233) Pause Young (Normal) (G1 Evacuation Pause) 411M->411M(1024M) 3.796ms
[1.174s][info][gc] GC(234) Pause Young (Normal) (G1 Evacuation Pause) 412M->412M(1024M) 3.912ms
[1.179s][info][gc] GC(235) Pause Young (Normal) (G1 Evacuation Pause) 413M->413M(1024M) 4.170ms
[1.184s][info][gc] GC(236) Pause Young (Normal) (G1 Evacuation Pause) 414M->414M(1024M) 3.653ms
[1.187s][info][gc] GC(237) Pause Young (Normal) (G1 Evacuation Pause) 415M->415M(1024M) 2.958ms

总耗时 795.046毫秒

平均一次GC时间为 3.340529412毫秒

不同版本对比:

JDk版本总耗时(毫秒)平均耗时(毫秒)总耗时提升(相较于参与测试的上一版本)平均耗时提升(相较于参与测试的上一版本)
1.8864.58357.145318--
11989.5834.140514644-14%+42%+
17795.0463.34052941220%-19%+

对比后发现JDK11的GC总耗时高于JDK8,可能是由于GC完全发生在新生代,G1的并行full GC特性没有体现出来,但是平均耗时比JDK8少42%,JDK17不管是总耗时还是平均耗时,时间都缩短了不少。

4、多线程执行10000000(一千万)次输出,并收集结果

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService executor = Executors.newScheduledThreadPool(6);
    List<Future> futures = new ArrayList<>();
    long begin = System.currentTimeMillis();
    //循环10000000次
    for (int i = 0; i < 10000000; i++) {
        String finalI = String.valueOf(i);
        Future future = executor.submit(() -> {
            System.out.println("这是第" + finalI + "次输出");
            return 1;
        });
        futures.add(future);
    }
    for (Future future : futures) {
        future.get();
    }
    executor.shutdown();
    long end = System.currentTimeMillis();
    System.out.println("共耗时:" + (end - begin) + "毫秒");
}

JDK8:

...
这是第9999998次输出
这是第9999989次输出
这是第9999978次输出
这是第9999999次输出
共耗时:67473毫秒

JDK11:

...
这是第9999968次输出
这是第9999951次输出
这是第9999999次输出
这是第9999993次输出
共耗时:61542毫秒

JDK17:

这是第9999996次输出
这是第9999993次输出
这是第9999990次输出
这是第9999999次输出
共耗时:40210毫秒

不同版本对比:

JDk版本耗时(毫秒)提升(相较于参与测试的上一版本)
1.867473-
11615429%-
174021035%-

对比后发现JDK17在多线程的表现上也是最好的。

综上4种测试都说明了JDK17的确是目前速度最快的JDK版本,值得我们考虑升级。

最后放上规划调度引擎 OptaPlanner 项目负责人对 JDK 17、JDK 16 和 JDK 11 的性能基准测试进行了对比

在这里插入图片描述

####总结

基于 OptaPlanner 用例,这些基准测试表明:

对于 G1GC(默认),Java 17 比 Java 11 快 8.66%,比 Java 16 快 2.41%

对于 ParallelGC,Java 17 比 Java 11 快 6.54%,比 Java 16 快 0.37%

Parallel GC 比 G1 GC 快 16.39%

简而言之,最新的 JDK 更快,高吞吐量垃圾回收器比低延迟垃圾回收器更快。

  • 40
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值