分享

期待已久的Java 9 今日正式发布,新特性解读

 爱因思念l5j0t8 2017-09-23


人们期待已久的Java SE 9.0将在2017年9月21日发布,它会带来一些重要的变化。


JDK 9的核心变化就是引入了一种新的Java编程组件,也就是模块,按照Oracle的说法,它是一个可命名的、自描述的代码和数据集合。


模块技术的核心目标是减少Java应用和Java核心运行时环境的大小与复杂性。为此,JDK本身进行了模块化,Oracle希望通过这种方式提升性能、安全性和可维护性。


为了支持Java 9的模块,引入一种新的模块化JAR文件形式,按照这种形式会在其根目录中包含一个module-info.class文件。


Oracle同时提供了工具,允许我们组合和优化一组模块,形成自定义运行时的镜像(image),这样的镜像不必将整个Java运行时包含进来。


模块化所带来的其他变化包括从Java运行时镜像中移除了rt.jar和tools.jar。


InfoQ与Ben Evans进行了交流,以了解他对Java 9.0模块系统的看法,他是Java社区进程(JCP)执行委员会的成员。





Evans:我认为最急需重构的应用恰好就是最适合进行模块化的应用。


如果你已经备受Lava Flow / God Class / Stovepipe System地狱的折磨,而且你的利益相关方明确知道这一点,那么你可能更容易说服他们进行一次完整的底层重构,通过渐进式的努力形成一个完成的模块解决方案(而不是简单重构并迁移至Java 8)是值得去做的。



Oracle宣布Java 8会是一个长期支持的发布版本,会一直支持到2022年,因此Evans认为很多的应用将会停留在Java 8上,根本不会升级到Java 9。Evans补充说,有些应用可能会让开发和构建工具链使用Java 8版本,而在生产环境使用Java 9的运行时。





对特定类型的应用来说,这是很有帮助的。


例如,我曾经见到有的电子商务网站具有非常大的堆空间,其中包含了大约40G的字符串数据。Java 9的ompact Strings技术能够将这种类型的内存使用减半。这反过来又会对GC的性能带来积极的影响。


对于有些应用来说(这可能就包括大型的Solr安装环境及类似场景),单单这一项收益就值得将运行时升级到Java 9。



Java 9使用G1作为默认的垃圾收集器,替代了之前默认使用的Parallel GC。Evans对这项变化的评论:





这项变更是很重要的,因为相对于Parallel来说,G1会在应用线程上做更多的事情,而Parallel几乎没有在应用线程上做任何事情,它基本上完全依赖GC线程完成所有的内存管理。


这意味着切换到G1将会为应用线程带来额外的工作,从而直接影响到应用的性能。


在很多(甚至可以说大多数)场景中,这种额外的性能损耗都不是什么问题。


但是,在这方面,我确实也曾经见过从Parallel切换到G1时,有一定比例的工作负载会引起性能的下降。


对于这些应用来说,这种性能下降是无法接受的,所以他们无法切换至G1收集器。随着G1成为默认的收集器,这将会影响到升级至Java 9的每个应用。



对于大型的代码库是否需要重构为模块的形式,InfoQ询问了Martijn Verburg的意见,他是JClarity的CEO,也是伦敦Java用户组(Java User Group)的联合组织者。





Verburg:需要这样做,另外,我还希望你要处理的大型代码库已经按照一定的模块化结构语义进行了拆分,不管你采用的是OSGi、Maven模块、JBoss模块,还是采用简单的内部规则,将包和接口的结构划分出清晰的边界都可以。



Verburg给出了一些通用的模块化建议,并且指出了开发人员在采用Java 9模块系统时,需要注意的一些事情:


  • 阅读Paul和Sander的图书“Java 9 modularity”:它是本权威指南,提到了所有需要注意的地方,阐述了模块、包以及JAR之间如何运行的关联关系;

  • 在模块边界的地方,使用定义良好的接口并且针对这些接口编程;

  • 不要拆分包(split package),也就是说一个包不要分散到两个模块中。Adopt OpenJDK有个探测工具,我们可以用它来探测已有的代码;

  • 确保不要存在循环依赖(Jigsaw不允许这样);

  • 模块在源码的布局上与我们已习惯的方式有所不同,需要确保构建工具能够进行对应的处理;

  • Jigsaw不支持多版本。


按照Verburg的说法,核心要点在于处理循环依赖、拆分包的问题,并确保针对接口进行编码。在尝试使用Jigsaw模块化重构之前,针对已有的代码库,这些工作需要预先完成。他还澄清了一个误解,那就是只有模块化的应用才能在Java 9上运行。





由于误解,在这方面有一种FUD(恐惧、不确定和怀疑)情绪,有人误认为在Java 9上运行的必须是模块化的应用。事实并非如此,我们可以将已有的基于类路径的应用直接在Java 9上运行。

这里会有一些新的安全限制,因此我们需要设置一些特定的运行时标记(除非你重构代码,使用更安全的方式来访问Java的内部资源),即便如此,默认的行为也只是警告,而不是完全阻止我们(Java 10的限制会更严格)。



Verburg认为Jigsaw会是一个基石,会让Java的演进更快,这要归功于Mark Reinhold、Alan Bateman、Mandy Chung以及Jigsaw团队的其他成员多年来不知疲倦的工作,正是他们的努力使这一切得以实现。


Java 9还引入了jshell工具。这个命令行环境为Java平台带来了读入-求值-打印-循环(Read-Eval-Print-Loop,REPL)功能。它的目的在于以即时结果和反馈的形式,简化原型的实现并帮助我们探索语言在编码时的可选项。


Verburg和Evans看到Java 9中包含了jShell都非常兴奋,但令他们失望的是,HTTP/2只是作为Java 9的一个孵化模块(incubator module)提供的。鉴于社区对这项特性的兴趣和提供的帮助,Evans认为Oracle应该投入足够的工程资源,将HTTP/2交付为GA版本。


JDK 9完整的变更列表可以在Oracle的站点上查阅。Oracle宣布会按照每六个月一次的节奏进行发布,意味着Java 9是最后一次“keystone”特性驱动的版本发布,这反映出了Oracle目前管理Java的特点。


Java下一阶段的演化将会按照更短的发布周期并且会按照更加面向特性的方式来发布。Java是否依然能够在服务端技术中占据领导者地位尚有待观察。


附相关解读:

在历经多次跳票之后,Java 9 终于在千呼万唤中正式发布。从这个版本开始,Java 将每半年发布一个版本。作为霸占编程语言排行榜鳌头多年的老牌语言,Java 9 中有哪些不得不说的新特性?Java 语言的未来又将如何?

针对 Java 9 新特性的介绍已经非常多了,我这里不想再做一个百科全书一样的列表,希望从不同角度简要点评部分特性。


Jigsaw


首先,谈到 Java 9 大家往往第一个想到的就是 Jigsaw 项目,这是一个雄心勃勃的项目。

大家知道,Java 已经发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越来越暴露出一些问题,比如 Java 运行环境的膨胀和臃肿,各种类库和工具在提供强大功能的同时,也越来越复杂,不同版本的类库交叉依赖导致 Jar Hell 等让人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。

但是由于兼容性等各方面的掣肘,对 Java 进行大刀阔斧的革新越来越困难,Jigsaw 从 Java 7 阶段就开始筹备,Java 8 阶段进行了大量工作,终于在 Java 9 里落地,有种千呼万唤始出来的意味。

Jigsaw 项目的目标是改进 Java SE 平台,使其可以适应不同大小的计算设备;改进其安全性,可维护性,提高性能;简化各种类库和大型应用的开发和维护。

这个项目的工作量和难度大大超出了初始规划。JSR 376 Java 平台模块化系统(JPMS, Java Platform Module System)作为 Jigsaw 项目的核心, 其主体部分被分解成 6 个 JEP(JDK Enhancement Proposals)

  • 200: The Modular JDK

  • 201: Modular Source Code

  • 220: Modular Run-Time Images

  • 260: Encapsulate Most Internal APIs

  • 261: Module System

  • 282: jlink: The Java Linker

可以看到这是一个庞大的系统工程,Java 的方方面面,包括 JDK 编译工具,运行时,Java 公共 API 和私有代码等等,完全是一个整体性的改变。

随着 Java 平台模块化系统的落地,开发人员无需再为不断膨胀的 Java 平台苦恼,例如,您可以使用 jlink 工具,根据需要定制运行时环境。这对于拥有大量镜像的容器应用场景或复杂依赖关系的大型应用等,都具有非常重要的意义。

从软件开发实践的角度,Java 语言层面提供对模块的支持,可以鼓励(当然在某种程度上也可以看作强制)更加规范的开发实践,利用业界在开发领域几十年的经验、教训总结出的最佳实践,促进 Java 生态的健康发展。比如,更加完善的隐藏实现细节,这不仅可以促进面向接口、约定的编程,也可以避免可能的安全风险等。

不过,换个角度来说,天下没有免费的午餐,由于 JPMS 是语言平台层面的支持,它并不是完全透明的,也就是说不管用户是否真的需要或从中收益,都会或多或少的受其影响。

对此,我们可以从 JPMS 评审中针对类似深度反射限制之类的激烈争吵中,深刻体会到。比如,针对反射访问控制,最终 Java 9 开发团队,采取了相对折中的办法,在反射领域默认保持 Java 8 的默认行为。Java 9 在兼容性方面,相比于过往的版本,采取了更大的容忍度。

不过,Java 9 的相当一部分特性仍然是对用户透明的。只要升级到 Java 9,不需要或者很少需要用户参与动作就能获益。比如,更加紧凑的字符串实现;改进的竞争锁机制;改进安全应用性能 ;利用特定 CPU 指令优化 GHASH 和 RSA 等等,这些都是开箱即用、触手可得的改进。


Java 9 值得关注的新特性


对于部分开发者来说,探究 Java 内部 API 或者平台底层能力是一件非常酷的事情,但这往往并不是非常容易,比如部分能力可能并没有在历史版本的公共 API 中暴露出来(比如 Unsafe 相关),或者需要特定领域的知识。在 Java 9 中,不要错过 JEP 193: Variable Handles 和 JEP 274: Enhanced Method Handles,JEP 259: Stack-Walking API,JEP 285: Spin-Wait Hints 等特性。

另外,Java 9 中还有很多承上启下的特性,为未来创新打下基础或者整合、规范现有碎片化的功能,我会介绍一些有代表性的新特性。

在 Java 虚拟机领域,JEP 271: Unified GC Logging 和 JEP 158:Unified JVM Logging,对各种 JVM 日志进行了统一,大家终于不用为各种碎片化的日志选项苦恼了。

Oracle 一直在努力提高 Java 启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到 Java,很多 C/C 开发者还是会不屑地评价为启动慢,吃内存。

简单说,这主要是因为 Java 编译产生的类文件是 Java 虚拟机可以理解的二进制代码,而不是真正的可执行的本地代码,需要 Java 虚拟机进行解释和编译,这带来了额外的开销。

JIT(Just-in-time)编译器可以在运行时将热点编译成本地代码,但是实际应用可能非常庞大,大型 Java 应用的预热往往非常耗时,而且非热点代码可能根本没有机会被 JIT 编译。

在 JDK 9 中, AOT(JEP 295: Ahead-of-Time Compilation)作为实验特性被引入进来,开发者可以利用新的 jaotc 工具将重点代码转换成类似类库一样的文件,这样会大大降低启动开销。

另外 JVMCI (JEP 243: Java-Level JVM Compiler Interface)等特性,对于整个编程语言的发展,可能都具有非常重要的意义,虽然未必引起了广泛关注。目前 Graal Core API 已经被集成进入 Java 9,虽然还只是初始一小步,但是完全用 Java 语言来实现的可靠的、高性能的动态编译器,似乎不再是遥不可及,这是 Java 虚拟机开发工程师的福音。

与此同时,随着 Truffle 框架和 Substrate VM 的发展,已经让个别信心满满的工程师高呼“One VM to Rule Them All!”, 也许就在不远的将来 Ploygot 以一种另类的方式成为现实。


谈谈 Java 的未来


前面简短地谈了谈 Java 9 中的一些令人激动的特性,Java 9 在取得这些进步的同时,那么在其的研发过程中有哪些教训,当前和未来遇到了那些挑战呢?

首先,就是如何更加快速、敏捷地进行创新。在 Java 9 的开发过程中, 非常突出的一点就是,由于 Jigsaw 项目的延期,导致 Java 9 的发布一再推迟,这带来了很多负面影响。大批特性已经完成多时,却无法及时被实际应用采纳,开发者无法及时地从中获益,也很难尽早发现和反馈可能存在的问题或改进。这不禁让人反思 Java 传统的研发模式的局限性。

针对这些情况,Java 首席架构师 Mark Reinhold 已经发出倡议,建议从传统的以特性驱动的发布周期,转变为以时间驱动的(6 个月为周期)发布模式,并逐步的将 Oracle JDK 原有商业特性进行开源,Java Flight Recorder 等杀手级工具和特性,一定会大受开发者的欢迎。针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(long term support)。

第二,随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发生翻天覆地的变化,不仅对 Java 的发展速度提出了更高要求,也深刻影响着 Java 技术的发展方向。传统的大型企业或互联网应用,正在被云端,容器化应用、模块化的微服务甚至是函数(FaaS, Function-as-a-Service)所替代。

Java 需要在新的计算场景下,改进开发效率。这话说的有点笼统,我谈一些自己的体会,Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等,但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向,例如,JEP 286: Local-Variable Type Inference;持续改进并发计算框架,Java 的并发特性非常强大和系统,但某种程度上过于复杂,在今年的 JVMLS 上,阿里巴巴 AJDK 组介绍了利用协程改进并发的实践,这是一个令人眼前一亮的创新;Java 非常需要更加友好的本地代码支持,相关的特性有很多好的想法和尝试,比如 Panama 项目;Value Types 和改进的泛型,有兴趣可以参考 Valhalla 项目。

最后,进一步改进启动和运行性能、优化计算资源使用。目前,相当一部分的 Java 类库和虚拟机特性都是针对长时间、大数据量、高并发等复杂任务进行的优化,但是在部分云计算场景中,比如越来越引起大家关注的 FaaS 应用,短时间、无状态的函数正在成为常见的计算单元。那么在这种场景下,Java 必须进行相应的改进和创新,才能保持和强化目前在软件开发领域的竞争力。比如,提高 Java 运行时启动速度,尤其是在容器环境的初始化表现;保证 CPU 等计算资源调度能力能够适应容器环境的新情况,最直接的就是 Java 平台需要支持基于 cgroup 等技术的资源管理;针对新场景下的 GC 优化;如何提高数据密度和计算效率等等。

以上很多方面往往不是孤立的,也不是非常简单就可以完成的,很多改进都是依赖于相关语言基础技术的进步和突破,Java 的进步需要持之以恒的耐心和持续的努力与投入。

最后,欢迎大家能够参与到 OpenJDK 社区,Java 是大家的,欢迎您向 OpenJDK 提供建议、意见或者直接提交自己的改进,在社区中听见越来越多的来自中国的声音是非常令人高兴的事情,让我们携手促进 Java 的创新和发展。



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多