JDK8飞到JDK17版本介绍和踩坑记录

【开发云】年年都是折扣价,不用四处薅羊毛 https://dev.csdn.net/activity?utm_source=sale_source&sale_source=wrTgVw3Pi8

 

为什么是JDK17

  1. SpringBoot3和Spring6的最低依赖就是JDK17!
  2. JDK17就是官方保证会长期支持的版本。
  3. JDK 17 最多可以支持到 2029 年 9 月份。
  4. JDK 8 支持的时间更长,可以延长到 2030 年 12 月
  5. 从 JDK 诞生到现在,还在长期支持的版本主要有 JDK 7、JDK 8 、JDK 11以及 JDK 1,JDK 17 将是继 Java 8 以来最重要的LTS版本,是 Java 社区八年努力的成果。

从JDK8到JDK17的新特性

JDK9新特性(2017年9月)

  • 模块化
  • 提供了List.of()、Set.of()、Map.of()和Map.ofEntries()等工厂方法
  • 接口支持私有方法
  • Optional 类改进
  • 多版本兼容Jar包
  • JShell工具
  • try-with-resources的改进
  • Stream API的改进
  • 设置G1为JVM默认垃圾收集器
  • 支持http2.0和websocket的API

重要特性:主要是API的优化,如支持HTTP2的Client API、JVM采用G1为默认垃圾收集器。

JDK10新特性(2018年3月)

  • 局部变量类型推断,类似JS可以通过var来修饰局部变量,编译之后会推断出值的真实类型
  • 不可变集合的改进
  • 并行全垃圾回收器 G1,来优化G1的延迟
  • 线程本地握手,允许在不执行全局VM安全点的情况下执行线程回调,可以停止单个线程,而不需要停止所有线程或不停止线程
  • Optional新增orElseThrow()方法
  • 类数据共享
  • Unicode 语言标签扩展
  • 根证书

重要特性:通过var关键字实现局部变量类型推断,使Java语言变成弱类型语言、JVM的G1垃圾回收由单线程改成多线程并行处理,降低G1的停顿时间。

JDK11新特性(2018年9月)(LTS版本)

  • 增加一些字符串处理方法
  • 用于 Lambda 参数的局部变量语法
  • Http Client重写,支持HTTP/1.1和HTTP/2 ,也支持 websockets
  • 可运行单一Java源码文件,如:java Test.java
  • ZGC:可伸缩低延迟垃圾收集器,ZGC可以看做是G1之上更细粒度的内存管理策略。由于内存的不断分配回收会产生大量的内存碎片空间,因此需要整理策略防止内存空间碎片化,在整理期间需要将对于内存引用的线程逻辑暂停,这个过程被称为"Stop the world"。只有当整理完成后,线程逻辑才可以继续运行。(并行回收)
  • 支持 TLS 1.3 协议
  • Flight Recorder(飞行记录器),基于OS、JVM和JDK的事件产生的数据收集框架
  • 对Stream、Optional、集合API进行增强

重要特性:对于JDK9和JDK10的完善,主要是对于Stream、集合等API的增强、新增ZGC垃圾收集器。

JDK12新特性(2019年3月)

  • Switch 表达式扩展,可以有返回值
  • 新增NumberFormat对复杂数字的格式化
  • 字符串支持transform、indent操作
  • 新增方法Files.mismatch(Path, Path)
  • Teeing Collector
  • 支持unicode 11
  • Shenandoah GC,新增的GC算法
  • G1收集器的优化,将GC的垃圾分为强制部分和可选部分,强制部分会被回收,可选部分可能不会被回收,提高GC的效率

重要特性:switch表达式语法扩展、G1收集器优化、新增Shenandoah GC垃圾回收算法。

JDK13新特性(2019年9月)

  • Switch 表达式扩展,switch表达式增加yield关键字用于返回结果,作用类似于return,如果没有返回结果则使用break
  • 文本块升级 """ ,引入了文本块,可以使用"""三个双引号表示文本块,文本块内部就不需要使用换行的转义字符
  • SocketAPI 重构,Socket的底层实现优化,引入了NIO
  • FileSystems.newFileSystem新方法
  • ZGC优化,增强 ZGC 释放未使用内存,将标记长时间空闲的堆内存空间返还给操作系统,保证堆大小不会小于配置的最小堆内存大小,如果堆最大和最小内存大小设置一样,则不会释放内存还给操作系统

重要特性:ZGC优化,释放内存还给操作系统、socket底层实现引入NIO。

JDK14新特性(2020年3月)

  • instanceof模式匹配,instanceof类型匹配语法简化,可以直接给对象赋值,如if(obj instanceof String str),如果obj是字符串类型则直接赋值给了str变量
  • 引入Record类型,类似于Lombok 的@Data注解,可以向Lombok一样自动生成构造器、equals、getter等方法;
  • Switch 表达式-标准化
  • 改进 NullPointerExceptions提示信息,打印具体哪个方法抛的空指针异常,避免同一行代码多个函数调用时无法判断具体是哪个函数抛异常的困扰,方便异常排查;
  • 删除 CMS 垃圾回收器

JDK15新特性(2020年9月)

  • EdDSA 数字签名算法
  • Sealed Classes(封闭类,预览),通过sealed关键字修饰抽象类限定只允许指定的子类才可以实现或继承抽象类,避免抽象类被滥用
  • Hidden Classes(隐藏类)
  • 移除 Nashorn JavaScript引擎
  • 改进java.net.DatagramSocket 和 java.net.MulticastSocket底层实现

JDK16新特性(2021年3月)

  • 允许在 JDK C ++源代码中使用 C ++ 14功能
  • ZGC性能优化,去掉ZGC线程堆栈处理从安全点到并发阶段
  • 增加 Unix 域套接字通道
  • 弹性元空间能力
  • 提供用于打包独立 Java 应用程序的 jpackage 工具

JDK16相当于是将JDK14、JDK15的一些特性进行了正式引入,如instanceof模式匹配(Pattern matching)、record的引入等最终到JDK16变成了final版本。

JDK17新特性(2021年9月)(LTS版本)

  • Free Java License
  • JDK 17 将取代 JDK 11 成为下一个长期支持版本
  • Spring 6 和 Spring Boot 3需要JDK17
  • 移除实验性的 AOT 和 JIT 编译器
  • 恢复始终执行严格模式 (Always-Strict) 的浮点定义
  • 正式引入密封类sealed class,限制抽象类的实现
  • 统一日志异步刷新,先将日志写入缓存,然后再异步刷新

虽然JDK17也是一个LTS版本,但是并没有像JDK8和JDK11一样引入比较突出的特性,主要是对前几个版本的整合和完善。

重要特性详解

Java 模块化

  • 一个模块通常只是一个 jar 文件,在根目录下有一个文件module-info.class。
  • 要使用模块,请将 jar 文件包含到 modulepath而不是classpath. 添加到类路径的模块化 jar 文件是普通的 jar 文件,module-info.class文件将被忽略。

总结:模块化的目的,是让jdk的各个组件可以被分拆,复用和替换重写,比如对java的gui不满意,可以自己实现一个gui,对java的语法不满意,可以把javac替换成其他语言和其他语言的编译器,比如kotlin和kotlinc等,没有模块化,几乎很难实现,每次修改某个模块,总不能把整个jdk给重新编译一遍,再发布一个整个sdk吧,模块化可以帮助更有效的定制化和部署

本地变量类型推断

在Java 10之前版本中,我们想定义定义局部变量时。我们需要在赋值的左侧提供显式类型,并在赋值的右边提供实现类型:   

MyObject value = new MyObject();

在Java 10中,提供了本地变量类型推断的功能,可以通过var声明变量:

var value = new MyObject();

本地变量类型推断将引入“var”关键字,而不需要显式的规范变量的类型。

其实,所谓的本地变量类型推断,也是Java 10提供给开发者的语法糖。

虽然我们在代码中使用var进行了定义,但是对于虚拟机来说他是不认识这个var的,在java文件编译成class文件的过程中,会进行解糖,使用变量真正的类型来替代var

HTTP客户端API-响应式流实现的HttpClient

Java 9发布包含一个HttpClient实现作为实验性功能。它随着时间的推移而发展,现在是 Java 11 的最终功能。现在 Java 应用程序可以进行 HTTP 通信,而无需任何外部依赖。

作为JDK11中正式推出的新Http连接器,支持的功能还是比较新的,主要的特性有:

  • 完整支持HTTP 2.0 或者HTTP 1.1
  • 支持 HTTPS/TLS
  • 有简单的阻塞使用方法
  • 支持异步发送,异步时间通知
  • 支持WebSocket
  • 支持响应式流

HTTP2.0其他的客户端也能支持,而HttpClient使用CompletableFuture作为异步的返回数据。WebSocket的支持则是HttpClient的优势。响应式流的支持是HttpClient的一大优势。

HttpClient中的NIO模型、函数式编程、CompletableFuture异步回调、响应式流让HttpClient拥有极强的并发处理能力,所以其性能极高,而内存占用则更少。

语法糖

Stream API 改进

Collectors.teeing()

teeing 收集器已公开为静态方法Collectors::teeing。该收集器将其输入转发给其他两个收集器,然后将它们的结果使用函数合并。

添加Stream.toList方法(jdk16)

//现在可以这样写 List<Integer> twoList = list.stream() .map(Integer::parseInt) .toList();

Switch表达式改进

支持箭头表达式(jdk12预览 jdk14标准)

此更改扩展了switch 语句以便它可以用作语句或表达式。不必为break每个 case 块定义一个语句,我们可以简单地使用箭头语法。


boolean isWeekend = switch (day) {
  case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false; 
  case SATURDAY, SUNDAY -> true;
  default -> throw new IllegalStateException("Illegal day entry :: " + day);
};


int size = 3;
String cn = switch (size) {
    case 1 -> "壹";
    case 2 -> "贰";
    case 3, 4 -> "叁";
    default -> "未知";
};
System.out.println(cn);
//要使用此预览功能,我们必须在应用程序启动期间使用–enable-preview标志明确指示 JVM。

yield关键字(jdk13)

使用yield,我们现在可以有效地从 switch 表达式返回值,并能够更容易实现策略模式。


public class SwitchTest {
    public static void main(String[] args) {
        var me = 4;
        var operation = "平方";
        var result = switch (operation) {
            case "加倍" -> {
                yield me * 2;
            }
            case "平方" -> {
                yield me * me;
            }
            default -> me;
        };

        System.out.println(result);
    }
}

字符串

文本块JSON改进(jdk13)


String json = """ 
{     
    "name" : "Baeldung",     
    "website" : "https://www.alibaba.com/" 
} 
""";

更多的API

  • isBlank():如果字符串为空或字符串仅包含空格(包括制表符),则返回 true。注意与isEmpty() 不同,isEmpty()仅在长度为 0 时返回 true。
  • lines():将字符串拆分为字符串流,每个字符串包含一行。
  • strip() :分别从开头和结尾;
  • stripLeading()/stripTrailing()仅开始和仅结束删除空格。
  • repeat(int times):返回一个字符串,该字符串采用原始字符串并按指定的次数重复该字符串。
  • readString():允许从文件路径直接读取到字符串。
  • writeString(Path path):将字符串直接写入指定路径处的文件。
  • indent(int level):缩进字符串的指定量。负值只会影响前导空格。
  • transform(Function f):将给定的 lambda 应用于字符串。

instanceof 的模式匹配(jdk14出预览,jdk16最终确认)

Object obj = "大阳";
if (obj instanceof String t) {
    // TODO 此时t已经是String类型了
}

JVM

GC变化

  • JDK9: 设置G1为JVM默认垃圾收集器
  • JDK10:并行全垃圾回收器 G1,通过并行Full GC, 改善G1的延迟。目前对G1的full GC的实现采用了单线程-清除-压缩算法。JDK10开始使用并行化-清除-压缩算法。
  • JDK11:推出ZGC新一代垃圾回收器(实验性),目标是GC暂停时间不会超过10ms,既能处理几百兆的小堆,也能处理几个T的大堆。
  • JDK14 :删除CMS垃圾回收器;弃用 ParallelScavenge + SerialOld GC 的垃圾回收算法组合;将 zgc 垃圾回收器移植到 macOS 和 windows 平台
  • JDk 15 : ZGC (JEP 377) 和Shenandoah (JEP 379) 不再是实验性功能。默认的 GC 仍然是G1。
  • JDK16:增强ZGC,ZGC获得了 46个增强功能 和25个错误修复,控制stw时间不超过10毫秒

指标测试

吞吐量比较

        在吞吐量方面,Parallel 中 JDK 8 和 JDK 11 差距不大,JDK 17 相较 JDK 8 提升 15% 左右;

        G1 中 JDK 17 比 JDK 8 提升 18%;ZGC 在 JDK 11[2]引入,JDK 17 对比JDK 11 提升超过 20%。

延迟比较

        在 GC 延迟方面,在 Parallel 中 JDK 17 对比 JDK 8 和JDK 11 提升 40%;

        在 G1 中,JDK 11 对比 JDK 8 提升 26%,JDK 17 对比 JDK 8 提升接近 60%!ZGC 中 JDK 17 对比 JDK 11 提升超过 40%。

暂停时间对比

        JDK 17 中的 ZGC 远低于目标:亚毫秒级的暂停时间。

        G1 的目标是在延迟和吞吐量之间保持平衡,远低于其默认的目标:200 毫秒的暂停时间。

        ZGC 的设计会保证暂停时间不随堆的大小而改变,当堆扩大到 128GB 时的情况。从暂停时间的角度来看,G1比Parallel 更善于处理更大的堆,因为它能够保证暂停时间满足特定目标。

其他

密封类和接口(15预览 17正式)

在Java15之前,所有的类都可以没有限制地继承其他类--除非被继承类被声明为final类型,任何类都可以实现公共接口。

现在在Java15中,一个类或者接口可以使用修饰符sealed声明为密封类或者接口,来限制其继承类。

EdDSA 算法

EdDSA (Edwards-Curve Digital Signature Algorithm) 是在 Java 15 中通过JEP 339添加的另一种附加数字签名方案。与其他可用的签名方案相比,它提供了更好的性能和安全的签名。

升级问题经验

JEP320找不到javax.annotation.*包

在 Java9-deprecated,Java11 中引入了一个提案 JEP 320: Remove the Java EE and CORBA Modules (openjdk.org/jeps/320) 提案,移除了 Java EE and CORBA 的模块,如果项目中用到需要手动引入。比如代码中用到了 javax.annotation.* 下的包

<dependency>
        <groupId>javax.annotation</groupId>
         <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version> 
</dependency>

使用了 sun.misc.* 下的包

sun.misc.BASE64Encoder替换工具即可

netty 低版本使用了 sun.misc.*

Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.netty.util.internal.PlatformDependent0

at io.netty.util.internal.PlatformDependent.getSystemClassLoader(PlatformDependent.java:694) ~[netty-all-4.0.42.Final.jar!/:4.0.42.Final]

升级版本即可 

lombok 使用了 com.sun.tools.javac.* 下的包

Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile (default-compile) on project encloud-common: Fatal error compiling: java.lang.ExceptionInInitializerError: Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors com.sun.tools.javac.processing.JavacProcessingEnvironment.discoveredProcs accessible: module jdk.compiler does not "opens com.sun.tools.javac.processing" to unnamed module

低版本有这个问题,升级最新版可以解决

kotlin 版本限制

[ERROR] Failed to execute goal org.jetbrains.kotlin:kotlin-maven-plugin:1.2.71:compile (compile) on project encloud-core: Compilation failure [ERROR] Unknown JVM target version: 17 [ERROR] Supported versions: 1.6, 1.8

1.6.0版本支持java17

JVM移除参数

移除参数如下


CMSDumpAtPromotionFailure,
CMSPrintEdenSurvivorChunks,
GlLogLevel,
G1PrintHeapRegions, 
G1PrintRegionLivenessInfo, 
G1SummarizeConcMark,
G1SummarizeRSetStats,
G1TraceConcRefinement,
G1TraceEagerReclaimHumongousObjects, 
G1TraceStringSymbolTableScrubbing,
GCLogFileSize, NumberofGCLogFiles, 
PrintAdaptiveSizePolicy,
PrintclassHistogramAfterFullGC,
PrintClassHistogramBeforeFullGC,
PrintCMSInitiationStatistics
PrintCMSStatistics, 
PrintFLSCensus,
PrintFLSStatistics,
PrintGCApplicationConcurrentTime
PrintGCApplicationStoppedTime,
PrintGCCause,
PrintGCDateStamps, 
PrintGCID,
PrintGCTaskTimeStamps,
PrintGCTimeStamps,
PrintHeapAtGC,
PrintHeapAtGCExtended, 
PrintJNIGCStalls,
PrintOldPLAB
PrintParallel0ldGCPhaseTimes, 
PrintPLAB,
PrintPromotionFailure,
PrintReferenceGC, 
PrintStringDeduplicationStatistics, 
PrintTaskqueue,
PrintTenuringDistribution, 
PrintTerminationStats,
PrintTLAB,
TraceDynamicGCThreads,
TraceMetadataHumongousAllocation,
UseGCLogFileRotation,
VerifySilently

GC-log参数调整

旧参数

新参数

-XX:+PrintGCDetails

-Xlog:gc*

-XX:+PrintGC

-Xlog:gc

-Xloggc:

-Xlog:gc:

file=

完整参数变迁

反射+私有API调用问题

在 Java8 中,没有人能阻止你访问特定的包

只要 setAccessible(true) 就可以了。Java9 模块化以后,一切都变了,只能通过 --add-exports 和 --add-opens 来打破模块封装

  • --add-opens 导出特定的包
  • --add-opens  允许模块中特定包的类路径深度反射访问

添加一个新的JVM参数: --illegal-access

该参数有四个可选值:

  • permit:默认值,允许通过反射访问,因此会提示像上面一样的警告,这个是首次非法访问警告,后续不警告
  • warn:每次非法访问都会警告
  • debug:在warn的基础上加入了类似e.printStackTrace()的功能
  • deny:禁止所有的非法访问除了使用特别的命令行参数排除的模块,比如使用

--add-opens排除某些模块使其能够通过非法反射访问

具体解决示例如下

--illegal-access=deny --add-opens java.base/java.lang=ALL-UNNAMED

idea中可在vm options中添加如下

如果使用Maven打包的时候还是会出现警告,可以在IDEA中的Maven配置中添加全局的Maven参数:

G1参数调整

不要配置新生代大小

这个在《JVM G1 源码分析和调优》一书里有详细的介绍,有两个主要的原因:

  • G1对内存的管理是不连续的,重新分配一个分区代价很低
  • G1 的需要根据目标停顿时间动态调整搜集的分区的个数,如果不能调整新生代的大小,那么 G1 可能不能满足停顿时间的要求

诸如 -Xmn, -XX:NewSize, -XX:MaxNewSize, -XX:SurvivorRatio 都不要在 G1 中出现,只需要控制最大、最小堆和目标暂停时间即可

更多精彩有待进一步探索整理吼吼……

借鉴大牛原文大家可以看下

一文详解|从JDK8飞升到JDK17,再到未来的JDK21

从 Java 8 升级到 Java 17 踩坑全过程,建议收藏!

解决JDK9以上的非法反射访问警告 - 知乎

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值