随着2022年jdk11的使用率首次超过jdk8,要开始关注jdk11的一些新特性,这里面最主要的,根据网上的资料,分为这几块。
1.完全支持Linux容器(包括docker)
许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的企业应用程序)都可以在Docker容器中运行。
但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。
这是因为Java应用程序没有意识到它正在容器中运行。
随着Java 10的发布,这个问题总算得以解决,JVM现在可以识别由容器控制组(cgroups)设置的约束。
可以在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括:
- 遵守容器中设置的内存限制
- 在容器中设置可用的CPU
- 在容器中设置CPU约束
Java 10的这个改进在Docker for Mac、Docker for Windows以及Docker Enterprise Edition等环境均有效。
2.ZGC
GC是java主要优势之一。 然而, 当GC停顿太长, 就会开始影响应用的响应时间。 消除或者减少GC停顿时长, java将对更广泛的应用场景是一个更有吸引力的平台。 此外, 现代系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分利用这些内存, 并且无需长时间的GC暂停时间。
ZGC, A Scalable LowLatency Garbage Collector(Experimental)ZGC, 这应该是JDK11最为瞩目的特性,没有之一。 但是后面带了Experimental,说明这还不建议用到生产环境。
ZGC 是一个并发, 基于region, 压缩型的垃圾收集器, 只有root扫描阶段会STW(stop the world), 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长。
特点:
- GC暂停时间不会超过10ms;
- 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG);
- 和G1相比, 应用吞吐能力不会下降超过15%;
- 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础;
- 初始只支持64位系统;
ZGC的设计目标是:
支持TB级内存容量, 暂停时间低(<10ms) , 对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制, 以支持不少令人兴奋的功能, 例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存) ,或压缩堆。
JDK11生产服务器有博主进行了测试:总体来说还处在实验阶段。
https://www.jianshu.com/p/78fcab6c318c
3.全新的 HTTP 客户端 API
HTTP,用于传输网页的协议,早在1997年就被采用在目前的1.1版本中。直到2015年, HTTP2才成为标准。
- HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。
- HTTP/1.1依赖于请求/响应周期。 HTTP/2允许服务器“push”数据:它可以发 送比客户端请求更多的数据。这使得它可以优先处理并发送对于首先加载网 页至关重要的数据。
- 这是 Java 9 开始引入的一个处理 HTTP 请求的的 HTTP Client API, 该API 支持同步和异步, 而在 Java11 中已经为正式可用状态, 你可以在java.net 包中找到这个 API。它 将 替 代 仅 适 用 于 blocking 模 式 的 HttpURLConnection( HttpURLConnection是在HTTP 1.0的时代创建的, 并使用了协议无关的方 法) , 并提供对WebSocket 和 HTTP/2的支持。
HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
// 同步
HttpResponse<String> response = client.send(request, responseBodyHandler);
String body = response.body();
System.out.println(body);
// 异步
CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
//这里会阻塞
HttpResponse<String> response1 = sendAsync.get();
System.out.println(response1.body());
// 以下是结合异步优化的写法
HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
//这里使用JDK8的CompletableFuture 进行了异步编程
CompletableFuture<HttpResponse<String>> sendAsync =thenAccept
client.sendAsync(request, responseBodyHandler);
sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);
HttpResponse<String> response = sendAsync.get();
String body = response.body();
System.out.println(body);
4.模块化(Module)
1.什么是模块化?
模块化就是增加了更高级别的聚合,是Package的封装体。Package是一些类路径名字的约定,而模块是一个或多个Package组成的封装体。
java9以前 :package => class/interface。
java9以后 :module => package => class/interface。
那么JDK被拆为了哪些模块呢?打开终端执行java --list-modules查看。
lixiaoshuang@localhost ~ java --list-modules
java.base@11.0.2
java.compiler@11.0.2
java.datatransfer@11.0.2
java.desktop@11.0.2
java.instrument@11.0.2
java.logging@11.0.2
java.management@11.0.2
java.management.rmi@11.0.2
java.naming@11.0.2
java.net.http@11.0.2
java.prefs@11.0.2
java.rmi@11.0.2
java.scripting@11.0.2
java.se@11.0.2
java.security.jgss@11.0.2
java.security.sasl@11.0.2
java.smartcardio@11.0.2
java.sql@11.0.2
java.sql.rowset@11.0.2
java.transaction.xa@11.0.2
java.xml@11.0.2
java.xml.crypto@11.0.2
jdk.accessibility@11.0.2
jdk.aot@11.0.2
jdk.attach@11.0.2
jdk.charsets@11.0.2
jdk.compiler@11.0.2
jdk.crypto.cryptoki@11.0.2
jdk.crypto.ec@11.0.2
jdk.dynalink@11.0.2
jdk.editpad@11.0.2
jdk.hotspot.agent@11.0.2
jdk.httpserver@11.0.2
jdk.internal.ed@11.0.2
jdk.internal.jvmstat@11.0.2
jdk.internal.le@11.0.2
jdk.internal.opt@11.0.2
jdk.internal.vm.ci@11.0.2
jdk.internal.vm.compiler@11.0.2
jdk.internal.vm.compiler.management@11.0.2
jdk.jartool@11.0.2
jdk.javadoc@11.0.2
jdk.jcmd@11.0.2
jdk.jconsole@11.0.2
jdk.jdeps@11.0.2
jdk.jdi@11.0.2
jdk.jdwp.agent@11.0.2
jdk.jfr@11.0.2
jdk.jlink@11.0.2
jdk.jshell@11.0.2
jdk.jsobject@11.0.2
jdk.jstatd@11.0.2
jdk.localedata@11.0.2
jdk.management@11.0.2
jdk.management.agent@11.0.2
jdk.management.jfr@11.0.2
jdk.naming.dns@11.0.2
jdk.naming.rmi@11.0.2
jdk.net@11.0.2
jdk.pack@11.0.2
jdk.rmic@11.0.2
jdk.scripting.nashorn@11.0.2
jdk.scripting.nashorn.shell@11.0.2
jdk.sctp@11.0.2
jdk.security.auth@11.0.2
jdk.security.jgss@11.0.2
jdk.unsupported@11.0.2
jdk.unsupported.desktop@11.0.2
jdk.xml.dom@11.0.2
jdk.zipfs@11.0.2
2.为什么这么做?
大家都知道JRE中有一个超级大的rt.jar(60多M),tools.jar也有几十兆,以前运行一个hello world也需要上百兆的环境。
- 让Java SE程序更加容易轻量级部署。
- 强大的封装能力。
- 改进组件间的依赖管理,引入比jar粒度更大的Module。
- 改进性能和安全性。
3.怎么定义模块?
模块的是通过module-info.java进行定义,编译后打包后,就成为一个模块的实体。下面来看下最简单的模块定义。
4.模块的关键字
· open
用来指定开放模块,开放模块的所有包都是公开的,public的可以直接引用使用,其他类型可以通过反射得到。
open module module.one {
//导入日志包
requires java.logging;
}
· opens
opens 用来指定开放的包,其中public类型是可以直接访问的,其他类型可以通过反射得到。
module module.one {
opens <package>;
}
· exports
exports用于指定模块下的哪些包可以被其他模块访问。
module module.one {
exports <package>;
exports <package> to <module1>, <module2>...;
}
· requires
该关键字声明当前模块与另一个模块的依赖关系。
module module.one {
requires <package>;
}
· uses、provides…with…
uses语句使用服务接口的名字,当前模块就会发现它,使用java.util.ServiceLoader类进行加载,必须是本模块中的,不能是其他模块中的.其实现类可以由其他模块提供。
module module.one {
//对外提供的接口服务 ,下面指定的接口以及提供服务的impl,如果有多个实现类,用用逗号隔开
uses <接口名>;
provides <接口名> with <接口实现类>,<接口实现类>;
}
5.其他
包含STRING的增加api、Stream的增强处理、var关键字、Jshell直接编译等,我觉得这些都不是很重要,属于锦上添花的功能需,要进行了解的话可以参考以下博主的详情描述。
感谢以下博主,部分引用自以下博主。
https://zhuanlan.zhihu.com/p/94459000
https://www.cnblogs.com/niujifei/p/15079097.html
6.是否切换到JDK11
- G1 GC平均速度通过Java 8切换到 Java 11 就有 16% 的改进,但是大部分项目都用不到,一些高实时性的游戏可以用;
- .Java 11支持源文件直接运行;
- 已完成项目不建议升级jdk11,或者新项目需要依赖现有代码,不建议升级jdk11,因为升级版本涉及到大量的旧代码移植,代码重写,架构重构,全量测试;
- 如果jdk8满足开发需求,并且需依赖现有以JDK8开发的代码,建议还是以jdk8进行开发,否则如果选用jdk11可能面临旧代码重写,架构重构,以及一些不知道的隐形依赖;
- 系统追求的是稳定并非技术,jdk8已被广泛验证非常稳定,而且目前主流开发版本确实也是8,技术还是要服务于业务,稳定大于一切;
- 从jdk8往上升级会出现一些jar依赖的改变,模块化带来的反射问题,classload的变化导致某些问题;
- Spring,Spring Boot,Spring Cloud,Dubbo,Guava,Jackson,Tomcat,JUnit等等项目都适配了JDK11,并且经历了生产环境的检验,才可以考虑是否将JDK8换成JDK11;