openjdk17新特性
背景
亚马逊通生态客户要求使用jdk17,而目前idaas的架构使用的是openjdk11版本,需要调研jdk17的特点以及与jdk11的不同点,是否能无感升级?
标黄的部分可以重点关注下
306: Restore Always-Strict Floating-Point Semantics
356: Enhanced Pseudo-Random Number Generators
为伪随机数生成器(PRNG)提供新的接口类型和实现,包括可跳转PRNG和一类额外的可拆分PRNG算法(LXM)。
382: New macOS Rendering Pipeline
使用Apple Metal API为macOS实现Java 2D内部渲染管道,作为现有管道的替代,现有管道使用已弃用的Apple OpenGL API。
391: macOS/AArch64 Port 可忽略
398: Deprecate the Applet API for Removal
概述:弃用Applet API进行删除。这基本上是无关紧要的,因为所有的web浏览器供应商要么已经取消了对Java浏览器插件的支持,要么宣布了这样做的计划。
403: Strongly Encapsulate JDK Internals
强烈封装JDK的所有内部元素,但关键的内部API(如sun.misc.Unsafe)除外。不再可能像JDK 9到JDK 16中那样,通过单个命令行选项放松内部元素的强大封装。
406: Pattern Matching for switch (Preview)
switch case的代码块增加了表达式和类型支持,对我们编码整洁度和效率有了提升。
407: Remove RMI Activation
删除远程方法调用(RMI)激活机制,同时保留RMI的其余部分。
409: Sealed Classes
密封类或接口只能由允许这样做的类和接口扩展或实现。通过对类的声明应用密封修饰符来密封类
410: Remove the Experimental AOT and JIT Compiler
删除实验性的基于Java的提前(AOT)和实时(JIT)编译器。这个编译器自推出以来几乎没有什么用处,维护它所需的努力也很重要。保留实验性的Java级JVM编译器接口(JVMCI),以便开发人员可以继续使用编译器的外部构建版本进行JIT编译
411: Deprecate the Security Manager for Removal
不推荐Security Manager,以便在将来的版本中删除。安全管理器可以追溯到Java 1.0。多年来,它一直不是保护客户端Java代码的主要手段,也很少用于保护服务器端代码。为了使Java向前发展,我们打算不推荐安全管理器与遗留的Applet API(JEP 398)一起删除。
412: Foreign Function & Memory API (Incubator)
FFM丰富了外部函数和堆外内存的api,提升了jvm和跨平台能力
414: Vector API (Second Incubator)
引入一个API来表达向量计算,在运行时可靠地编译为支持的CPU架构上的最优向量指令,从而实现优于等效标量计算的性能。
415: Context-Specific Deserialization Filters
382:(增强型功能)
概述:
使用Apple Metal API为macOS实现Java 2D内部渲染管道,作为现有管道的替代,现有管道使用已弃用的Apple OpenGL API。
目标:
1、为使用macOS Metal框架的Java 2D API提供功能齐全的渲染管道。
2、如果苹果从未来版本的macOS中删除了不推荐使用的OpenGL API,请做好准备。
3、确保Java应用程序的新管道的透明度。
4、确保实现与现有OpenGL管道的功能对等。
5、在选定的实际应用程序和基准测试中,提供与OpenGL管道相同或更好的性能。
6、创建适合现有Java2D管道模型的干净架构。
7、与OpenGL管道共存,直到其过时
356:(增强型功能)
概述:
为伪随机数生成器(PRNG)提供新的接口类型和实现,包括可跳转PRNG和一类额外的可拆分PRNG算法(LXM)。
改造细节:
我们提供四个新的专用RandomGenerator接口:
1、SplitableRandomGenerator扩展了RandomGeneration,还提供
名为split和splits的方法。可拆分性允许用户从现有的RandomGenerator中生成一个新的Random生成器,
该生成器通常会产生统计上独立的结果。
2、JumpableRandomGenerator扩展了RandomGeneration,还提供
方法名为jump和jumps。可跳跃性允许用户向前跳跃适度的平局次数。
3、LeapableRandomGenerator扩展了RandomGeneration,还提供
命名为leap和leaps的方法。可跳跃性允许用户在大量抽签前跳跃。
4、ArbitrilyJumpableRandomGenerator扩展了LeapableRandomGeneration,还提供了跳跃和跳跃的其他变体,允许指定任意跳跃距离。
398:(删除或回收功能)
概述:弃用Applet API进行删除。这基本上是无关紧要的,因为所有的web浏览器供应商要么已经取消了对Java浏览器插件的支持,要么宣布了这样做的计划。Java 9中的JEP 289以前不推荐使用Applet API,但不允许删除。
改造细节:
image风险和假设
如果这些API仍然存在,开发人员可以通过@SuppressWarnings(“remove”)注释或javac编译器的-Xlint:-remove命令行选项来抑制编译器警告。
403:(增强型功能)
概述:
强烈封装JDK的所有内部元素,但关键的内部API(如sun.misc.Unsafe)除外。不再可能像JDK 9到JDK 16中那样,通过单个命令行选项放松内部元素的强大封装。
目标:
1、继续提高JDK的安全性和可维护性,这是Project Jigsaw的主要目标之一。
2、鼓励开发人员从使用内部元素迁移到使用标准API,这样他们和他们的用户都可以轻松升级到未来的Java版本。
改造原因:
多年来,各种库、框架、工具和应用程序的开发人员使用JDK的内部元素的方式损害了安全性和可维护性。特别地:
java.*包的一些非公共类、方法和字段定义了特权操作,如在特定类加载器中定义新类的能力,而其他类则传递敏感数据,如加密密钥。尽管这些元素位于java.*包中,但它们都是JDK的内部元素。外部代码通过反射使用这些内部元素会使平台的安全面临风险。
sun.*包的所有类、方法和字段都是JDK的内部API。com.sun.*、jdk.*和org.*包的大多数类、方法和字段也是内部API。这些API从来都不是标准的,从来都不受支持,也从来没有打算供外部使用。外部代码使用这些内部元素是持续的维护负担。为避免破坏现有代码而保存这些API所花费的时间和精力,最好是将平台向前移动。
改造细节:
1、在Java9中,我们利用模块来限制对JDK内部元素的访问,从而提高了JDK的安全性和可维护性。模块提供强大的封装,这意味着模块外部的代码只能访问该模块导出的包的公共和受保护元素,并且
此外,受保护的元素只能从定义它们的类的子类中访问。
2、强封装适用于编译时和运行时,包括编译代码试图在运行时通过反射访问元素时。导出包的非公共元素和未导出包的所有元素都被称为强封装。
3、在JDK9和更高版本中,我们强烈封装了所有新的内部元素,从而限制了对它们的访问。然而,为了帮助迁移,我们故意选择在运行时不强封装JDK8中存在的内部元素。因此,类路径上的库和应用程序代码可以继续使用反射来访问java的非公共元素。*包装和太阳的所有元素。*对于JDK8中存在的包,这种安排被称为轻松强封装,是JDK9中的默认行为。
4、我们早在2017年9月就发布了JDK 9。JDK的大多数常用内部元素现在都有标准替换。开发人员有三年多的时间从JDK的内部元素迁移到标准API,如java.lang.invoke.MethodHandles。查找::defineClass,java.util。Base64和java.lang.ref.Cleaner。许多库、框架和工具维护人员已经完成了迁移,并发布了其组件的更新版本。与2017年相比,现在对宽松的强封装的需求减弱了,而且每年都会进一步减弱。
5、在2021发布的JDK 16中,我们朝着强大封装JDK的所有内部元素迈出了下一步。JEP 396对默认行为进行了强封装,但关键的内部API(如sun.misc)除外。不安全,仍然可用。在JDK16中,最终用户仍然可以选择宽松的强封装,以便访问JDK8中存在的内部元素。
6、我们现在准备在这一旅程中再迈出一步,取消选择轻松强封装的能力。这意味着JDK的所有内部元素都将被强封装,除了sun.misc.Unsafe等关键的内部API。
406:(增强型功能)
概述:
通过switch表达式和语句的模式匹配以及对模式语言的扩展来增强Java编程语言。将模式匹配扩展到switch允许根据多种模式对表达式进行测试,每种模式都有一个特定的操作,从而可以简洁而安全地表达复杂的面向数据的查询。这是JDK 17中的预览语言功能。
目标:
1、通过允许模式出现在case标签中,扩展switch表达式和语句的表达能力和适用性。
2、允许在需要时放松开关的历史零敌意。
3、引入两种新的模式:保护模式(允许使用任意布尔表达式来细化模式匹配逻辑)和圆括号模式(解决一些解析歧义)。
4、确保所有现有的switch表达式和语句继续编译而不发生任何更改,并以相同的语义执行。
5、不要引入与传统switch结构不同的具有模式匹配语义的新的类switch表达式或语句。
6、当大小写标签是模式时,不要使switch表达式或语句的行为与大小写标签为传统常量时不同。
改造细节:
在Java 16中,JEP 394扩展了instanceof运算符,以获取类型模式并执行模式匹配。这个适度的扩展允许简化熟悉的instanceof和cast习惯用法:
// Old code
if (o instanceof String) {
String s = (String)o;
... use s ...
}
// New code
if (o instanceof String s) {
... use s ...
}
Old code:
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
New code:
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
传统上,如果选择器表达式的计算结果为null,switch语句和表达式会抛出NullPointerException,因此必须在switch之外进行null测试:
Old code:
static void testFooBar(String s) {
if (s == null) {//会多加这一句非空判断
System.out.println("oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
当交换机只支持少数引用类型时,这是合理的。然而,如果switch允许任何类型的选择器表达式,并且case标签可以有类型模式,那么独立的null测试感觉就像是一种任意的区分,并且会带来不必要的样板和出错的机会。最好将空测试集成到交换机中:
New code:
static void testFooBar(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
既然支持了null这种类型,那么对象类型应该也可以
class Shape {}
class Rectangle extends Shape {}
class Triangle extends Shape { int calculateArea() { ... } }
static void testTriangle(Shape s) {
switch (s) {
case null:
break;
case Triangle t:
if (t.calculateArea() > 100) {
System.out.println("Large triangle");
break;
}
default:
System.out.println("A shape, possibly a small triangle");
}
}
还可以进一步简化:
static void testTriangle(Shape s) {
switch (s) {
case Triangle t && (t.calculateArea() > 100) ->
System.out.println("Large triangle");
default ->
System.out.println("A shape, possibly a small triangle");
}
}
写到这里大家肯定会问,现在可以支持Enum枚举类型了吧,回答当然可以。
record Point(int i, int j) {}
enum Color { RED, GREEN, BLUE; }
static void typeTester(Object o) {
switch (o) {
case null -> System.out.println("null");
case String s -> System.out.println("String");
case Color c -> System.out.println("Color with " + Color.values().length + " values");
case Point p -> System.out.println("Record class: " + p.toString());
case int[] ia -> System.out.println("Array of ints of length" + ia.length);
default -> System.out.println("Something else");
}
}
407:(删除或回收功能)
概述:删除远程方法调用(RMI)激活机制,同时保留RMI的其余部分。
删除原因:
RMI激活机制已过时并被废弃。Java SE 15中的JEP 385不赞成删除它。没有收到对该不赞成的评论。请参见JEP 385了解完整的背景、原理、风险和备选方案。
JavaEE平台包含一种称为JavaBeans激活框架(JAF)的技术。作为Eclipse EE4J计划的一部分,该项目后来被更名为雅加达激活。JavaBeans Activation和Jakarta Activation技术与RMI Activation完全无关,它们不受从Java SE中删除RMI Activition的影响。
改造细节:
删除java.rmi。Java SE API规范中的激活包
更新RMI规范,删除RMI激活
删除实现RMI激活机制的JDK库代码
删除RMI激活机制的JDK回归测试
删除JDK的rmid激活守护程序及其文档
风险和假设
对于JEP 385对RMI激活的否决,没有收到任何评论。然而,这并不构成对RMI活化没有依赖性的证据。移除RMI激活后,仍有可能破坏某些现有系统。反过来,这种系统的存在并不一定是保持RMI激活的有力理由。相反,移除RMI激活有助于强调需要为该系统制定更好的缓解计划。该计划不需要是紧急情况;使用RMI激活的现有系统将在具有长期或扩展支持的旧JDK版本上继续运行一段时间。
409:(增强型功能)
概述:**sealed****(密封) 和 permits(许可)修饰符
密封类或接口只能由允许这样做的类和接口扩展或实现。通过对类的声明应用密封修饰符来密封类。然后,在任何扩展和实现子句之后,permits子句指定允许扩展密封类的类。例如,下面的Shape声明指定了三个允许的子类:
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square { ... }
使用密封的类和接口增强Java编程语言。密封类和接口限制了哪些其他类或接口可以扩展或实现它们。
历史:
密封类由JEP 360提出,并在JDK 15中作为预览功能提供。它们由JEP 397再次提出并进行了改进,并作为预览功能在JDK 16中提供。本JEP建议在JDK17中最终确定密封类,而不改变JDK16。
目标:
1、允许类或接口的作者控制负责实现它的代码。
2、提供一种比访问修饰符更具声明性的方式来限制超类的使用。
3、通过为模式的详尽分析提供基础,支持模式匹配的未来方向。
410:(删除或回收功能)
概述:
删除实验性的基于Java的提前(AOT)和实时(JIT)编译器。这个编译器自推出以来几乎没有什么用处,维护它所需的努力也很重要。保留实验性的Java级JVM编译器接口(JVMCI),以便开发人员可以继续使用编译器的外部构建版本进行JIT编译。
删除原因:
提前编译(jaotc工具)通过JEP 295作为一个实验特性被纳入JDK 9。jaotc使用Graal编译器进行AOT编译,Graal编译器本身是用Java编写的。
Graal编译器通过JEP 317作为JDK 10中的实验JIT编译器提供。
自从这些实验特性被引入以来,我们几乎没有看到它们的使用,维护和增强它们所需的努力非常重要。Oracle发布的JDK16版本中没有包含这些功能,没有人抱怨。
改造细节:
卸下三个JDK模块:
jdk.aot-jaotc工具
jdk.internal.vm.compiler-Graal编译器
jdk.internal.vm.compiler.management-Graal的MBean
保留这两个与Graal相关的源文件,以便JVMCI模块(jdk.internal.vm.ci,JEP 243)继续构建:
src/jdk.internal.vm.compiler/share/classes/module-info.java
src/jdk.internal.vm.compiler.management/share/classes/module-info.java
删除与AOT编译相关的热点代码:
src/hots/share/aot-转储和加载aot代码
由#if INCLUDE_AOT保护的附加代码
最后,删除与Graal和AOT编译相关的测试以及生成文件中的代码
411:(删除或回收功能)
概述:
不推荐Security Manager,以便在将来的版本中删除。安全管理器可以追溯到Java 1.0。多年来,它一直不是保护客户端Java代码的主要手段,也很少用于保护服务器端代码。为了使Java向前发展,我们打算不推荐安全管理器与遗留的Applet API(JEP 398)一起删除。
目标:
为开发人员在未来版本的Java中删除Security Manager做好准备。
警告用户其Java应用程序是否依赖Security Manager。
评估是否需要新的API或机制来解决安全管理器所使用的特定狭窄用例,例如阻塞System::exit。
动机:
Java平台强调安全性。数据的完整性受到Java语言和VM内置内存安全的保护:变量在使用前被初始化,数组边界被检查,内存释放是完全自动的。同时,数据的机密性受到Java类库对现代加密算法和协议(如SHA-3、EdDSA和TLS 1.3)的可信实现的保护。安全是一门动态科学,因此我们不断更新Java平台,以解决新的漏洞并反映新的行业姿态,例如通过贬低弱加密协议。
随着对Java兴趣的增长,我们引入了签名小程序,以允许安全管理器信任远程代码,从而允许小程序访问与通过命令行上的Java运行的本地代码相同的资源。与此同时,Java类库正在迅速扩展——Java 1.1引入了JavaBeans、JDBC、Reflection、RMI和Serialization——这意味着可信代码可以访问重要的新资源,如数据库连接、RMI服务器和反射对象。允许所有受信任的代码访问所有资源是不可取的,因此在Java 1.2中,我们重新设计了安全管理器,将重点放在应用最小权限原则上:默认情况下,所有代码都将被视为不受信任的,受沙盒式控制的影响,并且用户将通过授予他们访问特定资源的特定权限来信任特定的代码库。理论上,类路径上的应用程序JAR在如何使用JDK方面可能比来自Internet的小程序更受限制。限制权限被视为一种限制代码体中可能存在的任何漏洞影响的方法——实际上,这是一种深度防御机制。
当时,安全管理器有雄心壮志,要防范两种威胁:恶意意图(尤其是远程代码)和意外漏洞(尤其是本地代码)。
由于Java平台不再支持小程序,远程代码的恶意威胁已经减弱。2017年,小程序API在Java 9中被弃用,然后在2021被弃用以在Java 17中删除,目的是在未来的版本中删除它。2018年,运行小程序的封闭源代码浏览器插件与封闭源代码Java Web Start技术一起从Oracle的JDK 11中删除。因此,Security Manager所保护的许多风险不再重要。此外,Security Manager无法防范现在非常重要的许多风险。安全管理器无法解决行业领导者在2020年发现的25个最危险的问题中的19个,因此XML外部实体引用(XXE)注入和不正确的输入验证等问题需要Java类库中的直接对策。(例如,JAXP可以防止XXE攻击和XML实体扩展,而序列化过滤可以防止恶意数据在造成任何损害之前被反序列化。)安全管理器也无法防止基于推测执行漏洞的恶意行为。
总之,对使用SecurityManager开发现代Java应用程序没有太大兴趣。基于权限做出访问控制决策既笨拙又缓慢,而且在整个行业中都不受欢迎。NET不再支持它。通过在Java平台的较低级别提供完整性(例如,通过加强模块边界(JEP 403)以防止访问JDK实现细节,并强化实现本身),以及通过进程外机制(如容器和管理程序)将整个Java运行时与敏感资源隔离,可以更好地实现安全性。为了推动Java平台的发展,我们将不赞成从JDK中删除传统的Security Manager技术。我们计划在多个版本中弃用和削弱SecurityManager的功能,同时为诸如阻止System::exit和其他被认为足够重要的用例等任务创建替代API
备注:In Java 17, we will
弃用大多数与Security Manager相关的类和方法以进行删除。
如果在命令行上启用了安全管理器,则在启动时发出警告消息。
如果Java应用程序或库动态安装Security Manager,则在运行时发出警告消息。
改造细节:
java.lang.SecurityManager
java.lang.System::{setSecurityManager, getSecurityManager}
java.security.{Policy, PolicySpi, Policy.Parameters}
java.security.{AccessController, AccessControlContext, AccessControlException, DomainCombiner}
image
412:(增强型功能)
概述:
介绍一个API,通过该API,Java程序可以与Java运行时之外的代码和数据进行互操作。通过有效地调用外部函数(即JVM外部的代码),并通过安全地访问外部内存(即不由JVM管理的内存),API使Java程序能够调用本地库并处理本地数据,而不会出现JNI的脆弱性和危险性。
Java平台一直为希望超越JVM并与其他平台交互的库和应用程序开发人员提供了丰富的基础。Java API以方便可靠的方式公开非Java资源,无论是访问远程数据(JDBC)、调用web服务(HTTP客户端)、服务远程客户端(NIO通道)还是与本地进程(Unix域套接字)通信。不幸的是,Java开发人员在访问一种重要的非Java资源时仍然面临着巨大的障碍:代码和数据与JVM在同一台机器上,但在Java运行时之外。
目标:
易用性-用一个优秀的纯Java开发模型取代Java本机接口(JNI)。
性能—提供与现有API(如JNI和sun.misc.Unsafe)相当的性能(如果不是更好的话)。
通用性-提供在不同类型的外部内存(例如,本机内存、持久内存和托管堆内存)上操作的方法,并随着时间的推移,适应其他平台(例如,32位x86)和用C以外的语言编写的外部函数(例如,C++、Fortran)。
安全-默认情况下禁用不安全操作,仅在应用程序开发人员或最终用户明确选择后才允许它们
改造细节:
ByteBufferAPI允许创建堆外分配的直接字节缓冲区,但它们的最大大小为2GB,不会立即释放。这些和其他限制源于这样一个事实,即ByteBuffer API不仅设计用于堆外内存访问,还设计用于字符集编码/解码和部分I/O操作等领域的批量数据的生产者/消费者交换。
414:(增强型功能)
概述:
引入一个API来表达向量计算,在运行时可靠地编译为支持的CPU架构上的最优向量指令,从而实现优于等效标量计算的性能。
对API的增强,以支持对字符的操作,例如UTF-8字符解码。具体来说,我们添加了用于在短向量和字符数组之间复制字符的方法,以及用于与整数向量进行无符号比较的新向量比较运算符。
API的增强功能,用于在布尔数组之间转换字节向量。
使用英特尔的短矢量数学库(SVML),对x64上的超越和三角车道运算提供内在支持。
Intel x64和ARM NEON实现的一般性能增强。
415:(增强型功能)
概述:
允许应用程序通过JVM范围的过滤器工厂配置上下文特定的和动态选择的反序列化过滤器,该过滤器工厂用于为每个反序列化操作选择过滤器