深入解读 Java 9 新特性

导读:经过三年的开发,Java 9 终于在 9 月 21 日发布了,我们一起来看一看 Java 9 中的新特性。另外,大家可能非常关心,Java 语言如何在云计算、大数据等浪潮中快速创新、保持竞争力,本文也介绍一下 Oracle 建议的时间驱动的新发布模式。

0?wx_fmt=png杨晓峰,OpenJDK Committer (JDK 9 和 JDK 10 项目),2011 年加入 Oracle 北京研发中心 Java 团队,目前领导 Java 核心类库北京团队,领导或参与了 Java 9,8 和 7 的核心类库等模块的新特性测试和开发工作。个人技术兴趣广泛,主要专注于 Java 等编程语言的发展,尤其是在云计算等前沿领域的应用和演进。

很高兴能有机会通过高可用架构社区和大家交流 Java 9 的主要新特性,由于内容较多,本文仅做一个概要性介绍,希望深入讨论某个方面的更多细节,可以在本文留言探讨。


今天的分享,除了简单介绍 Java 平台模块化项目(Jigsaw ),主要是如下四部分


  • Java 类库新特性,比如支持的新的标准、新的 API 等。

  • 语言和工具的变化。

  • JVM 领域新特性。

  • 简短介绍 Java 未来的发布模式。


基本上是按照目前 JDK 自身组成进行的划分。另外,在开始之前,介绍一个词汇JEP(JDK Enhancement Proposals),这是 Java 9 中 Oracle 为管理新特性引入的概念,后面谈到具体特性时大家会发现每一个都有相应的 JEP 号码。


首先,我们就来看看 Jigsaw 项目。


其核心是实现 Java 平台模块化系统(JSR 376,Java Platform Module System,经常会简化为JPMS),这是个庞大的项目,今天抛砖引玉简单介绍一下。


Mark Reinhold 曾经提到,Jigsaw 试图解决的是两个基础性的问题:


一 、脆弱的、容易出问题的 classpath:


Classpath 就是个非常简陋的容器,Jar 其实也不提供任何边界。所以,各部分的依赖关系是隐式的,或者说是隐晦的。在实际应用中,可能导致所谓的 Jar Hell 问题。例如有可能会出现,应用的不同部分依赖于同一类库的不同版本,进而导致类加载出现不可预计的行为。


ClassLoading 非常复杂,有些行为是不明确的。做过 Java EE 中间件的同学,可能都还记得,某些 Java EE 服务器以讨厌的 ClassNotFoundException、NoClassDefFoundError 之类问题而臭名昭著。


二、JDK 自身是一个大的单体应用,越来越臃肿。这是一个扩展性问题,JDK 自身很难按照不同需求进行定制或者优化,同时也会衍生出安全、维护等各种问题。


所以,Jigsaw 项目的目标,主要是两点:


  • 可靠的配置:明确模块边界和模块之间的依赖关系

  • 强封装性:通过封装模块内部私有细节,来避免不希望发生的依赖关系,也能避免一些安全问题等。


理论上,良好封装能让开发变得更敏捷,因为彼此之间的依赖、交互是明确的,可以鼓励基于契约的开发。


Jigsaw 项目主要被分解为六个JEP:


  • JEP 261: Module System,实现模块化系统;

  • JEP 200: The Modular JDK,将JDK自身进行模块化;

  • JEP 201: Modular Source Code,按照模块化的形式,重构源代码,因为现有代码并不是划分到一个一个的模块里的。

  • JEP 220: Modular Run-Time Images,重新设计JDK和JRE的结构,定义新的URI scheme操作模块,类和资源(jrt)。

  • JEP 260: Encapsulate Most Internal APIs,按照模块化封装性的要求,将不希望暴露的内部API封装起来,如果确实证明是广泛需要的,就作为公共API公开出来。

  • JEP 282: jlink: The Java Linker。新的link工具


从开发者的角度,代码和功能主要有下面这些变化:


  • 新增或修改工具和 API,用以在编译、链接和运行时支持模块。例如,基于 Module 的抽象,提供相应的 API 进行解析、配置或运行时操作等。

  • 工具方面,比如,最常用的 javac/java,增加了对模块的支持,支持 module path,另外还提供了各种针对模块的选择。

  • Java9 新增了一个可选的链接阶段(linking Phase),可以方便地创建最小依赖关系的 Java 运行时。


大家可以看看模块化以后的 Java SE:


0?wx_fmt=png

图 1 Java SE模块图


“java.base”是最基础的模块,核心类库的代码大部分都在这个模块。其他模块,从名字就可以大概地看出具体是哪个方面的类库。需要提醒的是,模块也可能是聚合其他模块组成,本身可能并不包含任何源代码,如“java.se”就是这样的聚合模块。

大家可能好奇,怎么查看具体某个 Java 模块的细节呢?在 Javadoc 里面就有很清晰的模块图和具体内容,比如:

http://download.java.net/java/jdk9/docs/api/java.se-summary.html


0?wx_fmt=png

图2 java.se 模块的 Javadoc 页面

Java 通常分为编译期或者运行时,在 Java 9 中,增加了可选的链接阶段(linking phase),可以用新增的 jlink 工具来定制运行时环境。完美地应用在实际应用场景,还需要更多大家的反馈和完善。


下面是一个例子


$ jlink --module-path jmods/ \

            --add-modules java.sql.rowset,java.activation \

            --output myimage

$ myimage/bin/java --list-modules

java.activation@9

java.base@9

java.datatransfer@9

java.logging@9

java.naming@9

java.security.sasl@9

java.sql@9

java.sql.rowset@9

java.xml@9

$ myimage/bin/java –m company.application


对于有 Java 使用经验的同学,这些都是非常容易理解的,可以很快上手。


第二,就是 Java 类库相关新特性。


我大致进行了分类,也主要关注在核心类库和安全类库方面:


  • 开发的工具API;

  • 新标准或者协议的支持;

  • 性能等方面的优化。 

首先,谈谈新的工具 API。


JEP 102:Process API Updates:


Java 历史版本以前对进程操作的支持是非常有限的,比如获取当前进程的 Pid 等信息,都往往要使用些 hack 手段。 在 Java9 里,新增了ProcessHandle 这种抽象,Process 也进行了相应扩展。开发者可以利用 ProcessHandle 访问 ProcessHandle.info 数据结构,来获取类似 pid、启动参数及累计的运行时间等信息。也可以更优雅的进行进程监控、销毁之类操作。例如利用 ProcessHandle.onExit 去在进程退出时做一些动作。


这是一段简单的例子:


ProcessHandle current = ProcessHandle.current();

current.info()

       .totalCpuDuration()

       .ifPresent(d -> System.out.println("Total cpu duration :" + d));

current.children()

       .forEach(p -> System.out.println("Pid:" + p.getPid()));


JEP 269: Convenience Factory Methods for Collections。


根据统计,在实际应用中,非常多的集合是有限元素的不可变示例。在历史版本中,需要非常繁琐的代码:


Set<String> set = new HashSet<>();

set.add("a");

set.add("b");

set.add("c");

set = Collections.unmodifiableSet(set);


利用新的静态工厂方法,一行代码就搞定!


Set<String> alphabet = Set.of("a", "b", "c");


注意,这是 unmodifiable 不是 immutable 哦。


JEP 166: Java 并发(Concurrency)API 更新。


提供了一个最小集合的 API 以支持 Reactive Stream,也就是所谓的 Flow API (Publisher, Subscriber, Processor)。开发者可以异步的方式处理数据流(Stream)而不是直接操作线程、同步等,进而避免很多并发问题,并且开发者可以利用 Back-Pressure 机制等,良好地(Memory-efficient)处理数据压力。目前,在新的 HTTP/2 Client API 里就有使用,感兴趣的话可以看看相关源代码。与此同时,改进了 CompletableFuture API,增强一些 time-based 的操作,并支持增强扩展性,比如实现一个子类替换 executor。


还有一些相对底层的 API,比如 JEP 259:Stack-Walking API,提供高效的标准 API去遍历 stack; JEP 193:Variable Handles, VarHandle 是把 Unsafe 之类的底层 fence 操作暴露出来。


下面,我们看看新的标准和协议。


安全方面的新标准和协议:


  • JEP 219: 支持Datagram Transport Layer Security (DTLS)。支持DTLS version 1.0 (RFC 4347) and 1.2 (RFC 6347),以提供安全的UDP传输。目前,基于UDP实现的类似SIP或者电子游戏协议,被证明是很有实际价值的。

  • JEP 229: 默认keystore格式从JKS替换为PKCS12。

  • JEP 244: TLS Application-Layer Protocol Negotiation(ALPN) Extension, 这是完整支持HTTP/2协议的前提之一。

  • JEP 249: 支持OCSP Stapling for TLS,减少证书状态验证的网络开销,提高性能。

  • JEP 273: 实现基于DRBG的SecureRandom,对于Deterministic Random Bit Generator (DRBG) 机制,您可以参考NIST 800-90Ar1相关文档。

  • JEP 287: SHA-3 Hash Algorithms。

网络方面,主要有JEP 110: HTTP/2 client API,支持 HTTP 1.1,HTTP/2 和 WebSocket协议,实现了全新的 HTTP client API,用于替换老旧的HttpURLConnection。这个 API 是一个高性能、简化、轻量级的 API,同时支持同步和异步操作模式,充分利用了 Reactive style 编程。但是,在 Java 9 中,还处于 incubator 阶段,未来版本进一步完善,并在恰当时机转变为标准 API。


我这里有些举例的代码片段:


HttpClient client = HttpClient.newBuilder()

               .sslContext(sslContext)

               .version(HTTP_2)

               .build();

    HttpRequest req = HttpRequest.newBuilder(uri)

               .POST()

               .build();

    client.sendAsync(req, abodyhandler)

              .thenApply(…);


可以看到广泛使用了Builder模式,fluent风格。

编码方面,Java SE 9 支持:


  • JEP 227: Unicode 7.0

  • JEP 267: Unicode 8.0

  • JEP 226: UTF-8 Property Files,在历史版本中,属性文件是基于ISO-8859-1,不支持的字符需要显示的替换为转义序列,Java 9 改进了属性文件和ResourceBundle API以支持 UTF-8


API性能优化,我想介绍下面几点:


  • JEP 254:Compact String,Java 9 中,修改 String 实现,以 byte[] 数组和一个编码标记替换 char[] (16 bits) 数组,这样 Latin 语系编码字符串会节省近半空间。这个修改对 API 的使用者完全透明。

  • JEP 232:提高安全应用性能。 开启security manager通常会导致10-15%的性能下降,Java 9 的改进显著降低了开销。如果有兴趣可以看看具体的代码,具体的优化包括用标准的并发容器替换自定义的同步逻辑,或者去掉不必要的检查等等。

  • JEP 24:利用CPU指令优化GHASH和RSA。这是充分利用Intel x64或者SPARC CPU的部分新指令。部分加密函数的性能提高非常显著,比如在 benchmark 中,相比于 JDK 8,AES 性能提高了至少 8 倍。这是相对保守的数据,测试数据大多在几十倍提升。


第三,语言和工具。


JEP 222: jshell: The Java Shell (Read-Eval-Print Loop -REPL)


Jshell 是一个有趣的新工具,可以简单易用的交互式执行 Java 代码。我们马上试试看,打开 command-line,然后执行“Path-to-JDK-9\bin\jshell”,就会出现一个交互界面,您可以输入 /help  来查看各种选项。


下面,我们实验一下前文介绍的 ProcessHandle API:


0?wx_fmt=png

图3 jshell示例


JEP 238: Multi-Release JAR Files,扩展了JAR文件格式,允许不同版本class文件共存,支持为不同JDK版本提供不同的代码实现。因为某些产品需要支持不同版本的Java,具体实现可能出现不可避免的区别,具体JAR文件结构如下图:


0?wx_fmt=png

图4 新的多版本JAR文件结构

Java 9提供了新的javac选项“--release”, "Javac --release N"在语义上等价于:"Javac -source N -target N -bootclasspath rtN.jar"。历史版本的信息被压缩保存在jdk里面,这个大大简化了多版本编译的问题。


JEP 225: Javadoc Search,这是个不起眼,但是非常实用的功能,个人非常喜欢。可以看我下面的示例,马上试试吧.


0?wx_fmt=png

图5 javadoc搜索


另外, 语言方面,Java 9 处理了 Project Coin / JSR 334 (Java SE 7)的一些遗留问题:


  • 允许 @SafeVargs 使用在private instance methods

  • 允许 effectively-final变量用于 try-with-resources语句

  • 如果类型推断有效,允许”<>”用于匿名类。

  • “_” 不再是合法的identifier名称,大家可以打开 IDE 试试,呵呵。

  • 支持private interface methods,这主要是适用于抽象interface method中的内部共用部分逻辑。


第四,JVM 新特性


JEP 248: 将 G1 作为默认垃圾收集器。目前 server 模式的默认选项是 ParallelGC(吞吐量优先)。一般认为通用场景中,延迟比吞吐量更能提高用户体验,G1 可以直接设定延迟目标,达到延迟 SLA 要求。而且最坏场景的延迟表现优于 CMS(设计原理导致碎片化问题)。


JEP 295: Ahead-of-Time Compilation,所谓的 AOT。利用新的编译工具 jaotc,可以直接把class编译成类似类库的文件。举例:


jaotc –output libHelloWorld.so HelloWorld.clas


然后使用时,只要:


java -XX:AOTLibrary=./libHelloWorld.so HelloWorld


AOT可以大大提高启动性能,目前还是实验阶段,支持Linux x64平台。


JEP 214: 移除过时的GC组合,移除在JDK 8中标记过时的Incremental CMS (iCMS),大家可以参考下图:


0?wx_fmt=png

图6 移除GC选项说明


注意,输入 Java 不支持的参数,不再是 warning,而是 error,直接在启动阶段就不再允许。

另外, GC 方面还有一个一定潜在影响的 JEP 291:Deprecate CMS,在 Java 9 开发阶段进行的沟通中,并没有社区成员承诺承担维护 CMS 的责任。建议 CMS 使用者可以开始考虑切换到 G1 等垃圾收集器。

JEP 158 / JEP 271: 统一日志( JVM / GC ),引入适用于 JVM 各个模块的通用日志机制,并在此基础上解决过于碎片化的 JVM 日志选项。

一些 JVM 性能优化工作,大家可能会感兴趣,比如:

  • JEP 143: 改进竞争锁( Contended Locking)改进高度竞争( high-contended)的 Java Object Monitor 性能。高度竞争可以理解为多个(大量)线程同时试图获取一个锁,在 Java9 里,实现了更快的 Java monitor enter, exit, notify / notifyAll 等。

  • JEP 285: Spin-Wait Hints。 Thread 新增一个方法 onSpinWait()。用于优化程序,尤其是运行在多核 CPU 时,在 busy-locking 时的性能表现,提高反应速度。但是注意,它需要利用 x86 的 pause 指令,所以前提是相应的 CPU 支持这个指令,否则,和现有行为没有区别。

前面谈了好多 Java9 新特性,我们看看 Java 未来发布模式的可能变化,主要参考了相关博客:

https://blogs.oracle.com/Java-platform-group/faster-and-easier-use-and-redistribution-of-java-se

该文透露出主要有几个重要信息:

  • 从 JDK9 GA, Oracle 计划发布 OpenJDK builds 基于 GPL。

  • Oracle 将逐步开源 Java Flight Recorder 之类的商业特性贡献给 Open JDK。

建议未来会切换为以时间驱动的发布模式,六个月的发布周期针对企业需求,以三年为周期发布 Long Term Support 版本

在刚刚结束的 JavaOne 大会上,除了 Java 领域本身的进一步开源和支持, Oracle 还现场源了非常棒的 serverless 框架, project Fn,该框架也将 Java 作为首选支持的编程语言,这些都是令人兴奋的积极变化。

Q&A

(作者注:下面是部分问答,特别强调,任何表述仅代表个人观点,希望集中于技术交流)

提问 1: Java9 怎么避免 jar hell?

杨晓峰:建议使用 JPMS 模块化,明确地定义模块的边界和依赖。当然,这样说比较范范,后续还是需要更多社区的实践和反馈。现在还有很多场景,并不在 JSR 376 的职责范围,比如,真正意义上的模块多版本。当前,加载不同版本的模块,可以尝试创建不同 Layer 的方式来实现。

提问 2: aot 可以直接编译到本地可执行文件吗 还是也需要 JRE 来执行? 还有现在 aot 的兼容性何如?

杨晓峰:不能直接编译成为可执行文件,这不是 jaotc 目前的职责;如果要定制和打包最终可执行的应用,目前还是需要 jlink 或者 javapackager 之类的工具。另外 aot 还是实验阶段,最初的版本只支持 Linux x64。

提问 3: Java 的 pattern matching 大概会在那个版本推出?

杨晓峰:这个特性 Brain Goetz 已经在社区里提过,有兴趣可以关注 OpenJDK 的相关邮件列表,获取最近进展,我无法提供任何有关版本和日程方面的信息。

提问 4:想请问 Oracle 和 JCP 之间是怎样的协作关系, JCP 有自己的 JSR, Oracle 推出了 jep,看 jep0 是说在提交 jsr 前给开发者一个更充分的准备,但不太清楚这两者之间如何协作, jep 会升级为 jsr 吗?可否介绍一下这几个概念

杨晓峰:坦白说 JCP 流程方面的东西我不是特别理解,也不好过多评价。 JEP 不是 JSR 的替代,它更是一个跟踪建议、设计、开发流程的概念。 JSR 是更正式的规范,其他公司可以根据这个 specification 做相应的参考实现。

提问 5: Java9 模块化能够很好的解决 class 的依赖问题,不过靠 maven 我们我们已经可以去解决依赖问题, Java9 的 module-info 对于开发应用有什么好处呢?对于 JDK 本身的好处似乎比较大?从应用层面来说,似乎现有的 maven 已经够用了。

杨晓峰:个人认为,这是不同层面的问题, JPMS 不能替代 build 工具的部分功能 , 而且,确实项目的初衷之一,就是解决 JDK 自身的模块化。对于好处,我可以举个,我现在写一个 serverless 应用,利用 jlink 工具去掉不必要的依赖, Docker image 只有几十兆大小,这在云计算环境并不是无所谓的。 Maven 也第一时间发布了对 Java 9 的支持, Java 社区和 Apache 等开源组织或者第三方公司有非常好的沟通。

提问 6: O 记是要让大家免费使用 JFR 了吗?

杨晓峰:免费不是技术问题,个人也无法回答。我谈到的是开源方面的信息,根据上面提到的博客( https://blogs.oracle.com/Java-platform-group/faster-and-easier-use-and-redistribution-of-java-se),未来会开源JFR等商业特性, Oracle JDK 和 Open JDK 做到 zero difference。可以看出 Oracle 对 Java 的支持是不予余力的,毕竟 Oracle JDK 有很多厉害的工具或者特性,具备很多同类产品没有的能力。

提问 7: Java 有计划对“协程”提供 runtime 级别的支持嘛?

杨晓峰:我注意到 OpenJDK 有相关项目提案,比如 http://cr.openjdk.Java.net/~rpressler/loom/Loom-Proposal.html

提问 8: kotlin 可能替代 Java 吗?你是怎么看待两个语言未来的发展方向?

杨晓峰:这个我不能做任何评价, " 臣妾真的做不到啊”。不要忽略了 , 全球至少 1200 万的 Java 开发人员和社区 , 还有海量的高质量类库、工具等等,这也代表了公司和机构天文数字的投入。历史上太多开源项目或语言起起伏伏,真正能够经久不衰的, Java 算是一个。


640?wx_fmt=png

54 个架构案例  49 位作者   2 年打磨

高可用架构』第 1 卷  10 月上市

点击 阅读原文 抢先预订

高可用架构

改变互联网的构建方式

640?wx_fmt=jpeg

长按二维码 关注「高可用架构」公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值