背景

版本发布

2021年3月16日 Java 17 作为继Java 11的下一个LTS版本正式发布,JDK 17 作为下一代 LTS 将提供至少到 2026 年的支持。

同业动态

2022年ThoughtWorks 26期技术雷达也提到,建议各个组织将Java 17纳入评估。

评估 82. Java 17

我们通常不会专门介绍某一语言的新版本,但我们还是想关注一下 Java 新的长期支持(LTS)版本——Java 17。尽管出现了前景不错的新特性,比如模式匹配等预览特性,但其实向新 LTS 版本升级的方案更应该吸引各个组织的兴趣。我们建议各个组织在 Java 有新的发布时对其进行评估,确保能够恰当地适配这些新特性和版本。尽管定期更新有利于简化开发并且方便管理,但许多组织却意外地并不经常更新语言的版本。希望通过 LTS 版本的升级以及开发团队对语言的定期更新,能够使生产软件免于因为“更新成本太高”而一直困在 Java 的过时版本上。

框架发展情况

Spring Framework 6和SpringBoot 3.0 将在2022年底正式发布,将基于Java 17构建。

Spring Boot 3.0.0 M1 Release Notes · spring-projects/spring-boot Wiki (github.com)

Minimum Requirements Changes

Spring Boot 3.0 makes the following changes to its minimum supported versions:

  • Gradle 7.3
  • Jakarta EE 9
  • Java 17
  • Kotlin 1.6
  • Spring Framework 6

其他团队落地情况

在此之前,我所在的团队使用的Java版本一直停留在Java8,今年经过其他开发团队的尝试,我们团队在4月份也正式决定在生产环境使用Java 17。

实践情况

开发组件版本

本次计划在内部运营管理系统重构中使用Java 17作为基础版本,其他核心组件版本如下

  • SpringBoot 2.6.6
  • mapstruct 1.4.2.Final
  • mybatis-plus 3.5.1
  • jdk 17.0.1

开发时SpringBoot 3.0还处于M2里程碑阶段,暂时不适合带入生产环境,等未来3.0 GA后再考虑升级

基础镜像

原本使用的基于jdk8 的Docker image也需要替换成基于jdk 17的image

带来的问题:

原本基于jdk8构建并注入到镜像中的opentelemetry-javaagent暂时不可用,等待后续基于jdk17 构建后才能重新启用

JVM参数

由于从Java11开始G1替代CMS作为默认的垃圾收集器,Java14开始移除了CMS GC。

故团队内定义的系统框架配置的GC调优参数基本废弃,并且可以使用Java 14新引入的JVM参数,例如-XX:+ShowCodeDetailsInExceptionMessages

开启前

Exception in thread "main" java.lang.NullPointerException
    at Book.main(Book.java:5)

开启后

Exception in thread "main" java.lang.NullPointerException: 
        Cannot assign field "book" because "shoopingcart.buy" is null
    at Book.main(Book.java:5)

语法特性

从Java8到Java17期间新增的语法特性很多,此处提及几个比较常用的

快速创建不可变集合

不可变对象具有许多优点,包括:

  • 可安全使用不受信任的库。
  • 线程安全。
  • 不需要支持变化判断,所有不可变的集合实现都比其可变的同级实现更节省内存。

Java9开始新增List、Map、Set等of方法快速创建不可变集合

List.of(prodStrategy, instStrategy)

实现源码

    static <E> List<E> of(E e1, E e2) {
        return new ImmutableCollections.List12<>(e1, e2);
    }

Java16开始,Stream类中也新增toList()方法,快速将流转成不可变集合

List<Long> ids = idList.stream().map(ContactId::id).toList();

实现源码

    default List<T> toList() {
        return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
    }

注意:如果需要将流转成可变集合,还是需要使用collect(Collectors.toList())方法

record类型

Java14开始新增record类型,用于表示不可变class,实践中可以方便的表示值对象,无需使用@Value注解

public record ContactId(@NotNull(message = "ID不能为空") Long id) {
}

instanceof模式匹配

Java14开始支持模式匹配,无需做强制类型转换

    private void configMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof MappingJackson2HttpMessageConverter);
        converters.add(new StringHttpMessageConverter(Charsets.UTF_8));
        converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
    }

创建类型安全的流

Java9 开始,新增ofNullable方法创建类型安全的流对象,避免在调用stream方法时报NPE

// query.getDescColumns()可能为null
List<OrderItem> descItems = Stream.ofNullable(query.getDescColumns())
                .flatMap(Collection::stream)
                .map(OrderItem::desc)
                .toList();

性能和安全提升

从同一段功能逻辑中测试发现,Java8和Java17在并发场景下性能差距达到2倍以上

并且同样的逻辑(使用了并发安全的工具)在并发量达到50000的时候在Java8中依然有线程安全问题,而Java17中不会有线程安全问题

外部测评结果

从规划调度引擎 OptaPlanner 项目(原文 (opens new window))对 JDK 17和 JDK 11 的性能基准测试进行了对比来看:

  1. 对于 G1GC(默认),Java 17 比 Java 11 快 8.66%;
  2. 对于 ParallelGC,Java 17 比 Java 11 快 6.54%;
  3. Parallel GC 整体比 G1 GC 快 16.39%

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

Last modification:October 31st, 2022 at 11:04 am
如果觉得我的文章对你有用,请随意赞赏