Java 12的时机已到! 热门JEP的评论


六个月过去了,这意味着该安装新的Java了 ! 这是一段漫长的旅程,很少有人走到尽头。 原始行来自有趣的JEP,但我们将讨论其余部分。


一切都好吗


Java新版本的发布是根据新的“加速”发布周期进行的,周期约为六个月。 确切的日期在项目页面上定义。 JDK 12有几个主要阶段:


  • 2018/12/13-减速的第一阶段(目前,从存储库的主分支中创建了一个fork);
  • 2019/01/17-减速的第二阶段(完成所有可能的事情);
  • 2019/02/07-候选版本(仅修复了最重要的错误);
  • 2019/03/19-发布,全面上市。 <-您在这里

这个时间表有什么? 是的,事实上,什么都没有-我们才刚刚走到终点,我们从全新的新鲜JDK 12高度观看了传统爱好者。


虫子! 惊慌! 一切都归根到底!



当一个新的非LTS版本问世时,通常每个人都不会对新功能一无所知。 如果一切都崩溃了,那就更有趣了。


当然,这里有很多错误,但是在JDK 12中却没有:)从jir来看,一切正常:



我将引用该请求,以便您确切地了解“规范”是什么:


project = JDK AND issuetype = Bug AND status in (Open, "In Progress", New) AND priority in (P1) AND (fixVersion in (12) OR fixVersion is EMPTY AND affectedVersion in (12) AND affectedVersion not in regexVersion("11.*", "10.*", "9.*", "8.*", "7.*", "6.*")) AND (labels is EMPTY OR labels not in (jdk12-defer-request, noreg-demo, noreg-doc, noreg-self)) AND (component not in (docs, globalization, infrastructure) OR component = infrastructure AND subcomponent = build) AND reporter != "Shadow Bug" ORDER BY priority, component, subcomponent, assignee 

当然, 在一般情况下, bug都是存在的地方,在如此庞大的项目中它们不会消失。 仅声称目前没有发现P1错误。


在特殊文件JEP 3:JDK Release Process中声明了与bug的更正式的通信,该文件归我们不朽的管家在Java海洋动荡的浪潮中拥有-Mark Reinhold。


特别值得提的 谁该责备该怎么办 如果您没有时间发布第12版票,该如何转让票证。 必须在bugtracker中放入标签jdk$N-defer-request ,其中N表示要从哪个版本进行转移,并留下注释,其第一行是Deferral Request 。 此外,所有此类请求的审查由相应领域和项目的负责人进行。


不能以这种方式忽略传递TCK的问题-确保Java仍然是Java,而不是像青蛙一样的东西。 jdk$N-defer-request label永远不会消失。 他们对违反不删除标签规则的人怎么做很有趣-我建议给豚鼠喂食。


但是,通过这种方式,您可以看到已将多少个错误移植到JDK13。让我们尝试以下查询:


 project = JDK AND issuetype = Bug AND status in (Open, "In Progress", New) AND (labels in (jdk12-defer-request) AND labels not in (noreg-demo, noreg-doc, noreg-self)) AND (component not in (docs, globalization, infrastructure) OR component = infrastructure AND subcomponent = build) AND reporter != "Shadow Bug" ORDER BY priority, component, subcomponent, assignee 

仅1件, JDK-8216039 :“带有BC和RSASSA-PSS的TLS破坏了ECDHServerKeyExchange”。 不厚 如果该论点仍然无济于事,那么作为您的律师,我建议您使用镇静剂。


底线是什么?



显然,大多数功能并不影响用户(Java程序员),而是影响OpenJDK本身的开发人员。 因此,以防万一,我将功能分为externalinternal 。 您可以跳过内部的内容,但是我很生气,我写了很多文字。


189: 雪兰多亚:低暂停时间的垃圾收集器(实验性)


外部特征 。 简而言之,当Java速度变慢时,人们不喜欢它,特别是如果SLA需要10-500毫秒量级的响应能力。 现在,我们有一个免费的低干扰GC,它试图在该范围的左边缘附近工作。 权衡是我们交换CPU和RAM以减少延迟。 髋关节标记和压缩阶段与实时应用程序线程并行工作。 剩下的小停顿是由于您仍然需要搜索和更新对象图的根。


如果以上都不对您有意义-没关系,Shenandoah 可以工作 ,无论您是否了解基础流程。


Alexei Shipilev,Christina Flood和Roman Kennke都在为此工作-您需要努力不认识这些人。 如果您通常了解GC的工作原理,但不知道开发人员可以做什么,我建议您看一下Leshina文章“ OpenJDK的自制垃圾收集器”JVM Anatomy Quarks系列的精彩翻译。 这很有趣


非常重要。 Oracle决定不将Sheandoah附带其任何发行版本-jdk.java.net上的发行版和oracle.com上的发行版均不提供。 鉴于Shenandoah是JDK 12的最重要功能之一,因此值得安装其他正式组件, 例如从Azul安装



230:微基准套件


内部特征 。 如果您曾经尝试编写微基准测试,那么您知道这是在JMH上完成的。 JMH是一个用于创建,组装,启动和分析Java和其他JVM语言的微基准的框架,您自己知道是谁编写的(所有匹配项都是随机的)。 不幸的是,并不是在“正常”应用程序世界中完成的所有事情都可以在JDK内部应用。 例如,我们不太可能在那里看到普通的Spring Framework代码。


幸运的是,从版本12开始,您至少可以使用JMH,并且已经编写了一组测试。 您可以在jdk/jdk/test/micro/org/openjdk/bench看到它(可以直接在浏览器中查看,此路径是链接)。


例如,这是GC测试的外观。


让我提醒您,我们这里没有StackOverflow,并且禁止在此处和此后使用复制粘贴中的代码,而不必阅读并遵守相应文件OpenJDK项目中的所有许可证,否则,您将很容易被起诉。

 @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) public class Alloc { public static final int LENGTH = 400; public static final int ARR_LEN = 100; public int largeLen = 100; public int smalllen = 6; @Benchmark public void testLargeConstArray(Blackhole bh) throws Exception { int localArrlen = ARR_LEN; for (int i = 0; i < LENGTH; i++) { Object[] tmp = new Object[localArrlen]; bh.consume(tmp); } } //... } 



325:开关表达式(预览)


外部特征 。 它将从根本上改变您编写长度超过两个屏幕的环形开关的方法。 看:


Virgin Java Switch与...


 int dayNum = -1; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: dayNum = 6; break; case TUESDAY: dayNum = 7; break; case THURSDAY: case SATURDAY: dayNum = 8; break; case WEDNESDAY: dayNum = 9; break; } 

不好的原因 :有很多字母,您可以跳过中断(特别是如果您是吸毒者或患有多动症的患者)。


... vs乍得Java Swtich表情!


 int dayNum = switch (day) { case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); break result; } }; 

优点 :字母少,安全,方便,新颖的功能。


奖励 :如果您是一个虐待狂,这将使您获得最大的满足,因为成千上万的IDE开发人员现在因该功能的支持而受苦。 是的,是吗? 您可以在4月6日的报告后抓到他然后轻轻地要求提供所有肮脏的细节。


这是预览功能,它将无法正常工作! 编译时,在javac您需要传递命令行选项--enable-preview --release 12 ,并通过java运行-仅--enable-preview标志。



334: JVM常量API


内部特征 。 开发人员希望操纵类文件。 您需要方便地执行此操作,这就是问题所在。 至少,这就是拥有这个JEP的布莱恩·戈茨(Brian Goetz)所说的:-)所有这些都是更大战场的一部分,但是目前我们不会深入。


每个Java类都有一个所谓的“常量池”,其中有一些值(如字符串和整数)或运行时实体(如类和方法)的转储。 您可以使用ldc- “ load costant ”指令深入研究此转储,因此所有这些垃圾都称为可加载常量。 调用动力学仍然有一种特殊情况,但没关系。


如果我们使用类文件,那么我们想方便地模拟字节码工具,因此-可加载常量。 第一个愿望是简单地创建相应的Java类型,但是如何为它们提供“活动”类CONSTANT_Class_info结构? Class对象取决于类加载的正确性和一致性,并且随着Java中类的加载,创建了一种地狱般的细菌。 首先,并非所有类都可以加载到VM中,但是您仍然需要对其进行描述!


考虑到所有这些细微之处,我想以某种方式管理类,方法以及鲜为人知的野兽(例如方法句柄和动态常量)。


通过引入新的基于值的符号链接类型(在JVMS 5.1方面 )可以解决此问题,每种符号链接都描述一种特定的常量类型。 仅从名义上描述,与加载类或访问问题无关。 它们位于java.lang.invoke.constant程序包中,不需要这样做,但是您可以在此处查看补丁。



340: 一个AArch64端口,而不是两个
外部特征 。 在JDK 9中,当Oracle和Red Hat同时将其ARM端口置于警报状态时,已经出现了一种奇怪的情况。 现在我们看到了故事的结局:Oraklov端口的64位部分已从上游移除。


您本可以很长一段时间来探究历史,但是有更好的方法。 BellSoft参与了该JEP的开发,其办公室位于圣彼得堡,紧邻Oracle的前办公室。


因此,我立即向BellSoft的首席技术官Alexey Voitilov求助:


“ BellSoft推出了Liberica JDK,除了x86 Linux / Windows / Mac和Solaris / SPARC外,它还支持ARM。从用于ARM的JDK 9开始,我们致力于改善服务器应用程序的AARCH64端口的性能,并继续支持32位ARM端口。因此,在发布JDK 11时,有一种情况是没有人支持Oracle(包括Oracle)的64位端口部分,并且OpenJDK社区决定删除它以专注于AARCH64端口。见,例如, JEP 315 ,这是我们 集成在JDK 11中),并且从JDK 12开始,它支持Oracle端口中的所有功能(最后一个是Minimal VM,我于9月集成),因此,我很乐意帮助Bob Vandette删除JDK 12中的这个基础知识。该社区在AARCH64上获得了一个端口,在ARM32上获得了一个端口,这无疑使它们更易于支持。”



341: 默认CDS存档


内部特征 。 问题在于,在Java应用程序的启动过程中,加载了数千个类,这使人感觉Java在启动时会明显变慢。 但是,有谁在撒谎,这不仅是一种“感觉”,而是如此。 为了解决远古时代的问题,人们实行了各种仪式。


类数据共享是一个世纪以来的功能,就像JDK 8 Update 40的一项商业功能一样。它允许您将所有启动垃圾打包到自己某种格式的存档中(您无需知道哪种格式),然后启动速度应用程序正在增加。 过了一会儿, JEP 310出现了:应用程序类-数据共享,它使我们不仅可以与系统类一起使用,还可以与应用程序类以相同的方式工作。


对于JDK类,它看起来像这样。 首先,我们使用java -Xshare:dump命令转储类,然后运行应用程序,告诉它使用以下缓存: java -Xshare:on -jar app.jar 。 一切,创业公司都有一些进步。 您知道此功能吗? 许多人仍然不知道!


在这里看起来很奇怪:为什么每次都-Xshare:dump地写-Xshare:dump即使在创建JDK发行版时此命令的默认结果还是可以预料的,还是要-Xshare:dump ? 根据文档 ,如果Java 8发行版是使用安装程序安装的,那么在安装时,它应该为您运行必要的命令。 就像,安装程序正在角落里悄悄地挖掘。 但是为什么呢? 以及如何处理分发,分发不是作为安装程序而是作为zip文件分发?


很简单:从JDK 12开始,链接之后,分发套件的创建者将立即生成CDS存档。 即使是夜间构建(前提是它们是64位本地语言,也不用于交叉编译)。


用户甚至不需要了解此功能的存在,因为从JDK 11开始, -Xshare:auto默认情况下-Xshare:auto启用状态,这样的归档文件将自动提取。 因此,仅更新到JDK 12的事实就可以加速应用程序的启动!



344: G1可终止的混合集合


内部特征 。 说实话 我对G1的工作一无所知 对GC功能的解释是一项艰巨的任务。 它需要从解释者和理解者两个角度了解其工作细节。 对于大多数人来说,GC就是鼻烟壶中的地狱,万一发生某些情况您可以作弊。 因此,必须以某种方式更简单地解释问题。


问题 :G1可能会更好。


好吧,问题在于GC是许多参数的折衷,其中之一就是暂停的时间。 有时候,暂停时间太长,因此可以取消它。


什么时候发生? G1确实分析了应用程序的行为,并根据其结论选择了工作的前端(表示为集合集 )。 当工作范围获得批准时,G1承诺一次坐下来不间断地顽固地收集收集集中的所有生物。 有时会花费太多时间。 从本质上讲,这意味着G1错误地计算了工作量。 您可以通过突然更改应用程序的行为来欺骗他,以便当太多的旧区域进入集合集中时,启发式方法将在不良数据之上起作用。


为了摆脱这种情况,G1通过以下机制进行了最终确定:如果启发式规则选择了错误的工作量,则G1逐步切换到增量垃圾收集,然后可以取消每个下一步(如果它不适合目标执行时间)。 增量收集(青年地区)的东西没有意义,因此,所有此类工作都在“强制性”块中突出显示,该块仍在连续进行。


与最终用户怎么办? 没什么,您不需要升级到JDK 12,一切都会变得更好。



346: 立即从G1返回未使用的承诺内存


内部特征 。 问题是,如果我们有一个没人主动使用的大块头,那么将所有这些不活动的内存返回操作系统似乎是公平的。 但是,在JDK 12之前,这没有发生。


为了实现其在允许的暂停长度方面的目标,G1执行了一组增量,并行和多阶段循环。 在JDK 11中,它仅在具有完整GC的情况下或在并行标记阶段才将已提交的内存提供给操作系统。 如果连接日志记录(-Xloggc:/home/gc.log -XX:+ PrintGCDetails -XX:+ PrintGCDateStamps),则此阶段显示如下:


 8801.974: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: occupancy higher than threshold, occupancy: 12582912000 bytes, allocation request: 0 bytes, threshold: 12562779330 bytes (45.00 %), source: end of GC] 8804.670: [G1Ergonomics (Concurrent Cycles) initiate concurrent cycle, reason: concurrent cycle initiation requested] 8805.612: [GC concurrent-mark-start] 8820.483: [GC concurrent-mark-end, 14.8711620 secs] 

有趣的是,G1会竭尽全力,并且并发周期仅从频繁分配和阻塞堆开始。 当没有人碰到臀部时,我们的情况正好相反。 G1划痕为操作系统提供内存的情况极少发生!


因此,每个人都将在这个问题上得分(“购买更多的RAM,这就像一个流氓!”)。如果不是,而是-各种各样的云和容器,这意味着利用率不足并损失大量资金。 你看,多么酷的报道 ,充满了痛苦。


解决方案是教导G1在这种特殊情况下表现良好,这是OpenJ9的Shenanda或GenCon已经知道的。 有必要确定髋关节利用率不高,从而减少髋关节的使用。 在Tomcat上进行的一些测试中,这使内存消耗减少了近一半。


最重要的是,该应用程序被视为处于非活动状态,或者自上次构建以来间隔(以毫秒为单位)且没有并发周期,或者一分钟的getloadavg()显示负载低于某个阈值。 一旦发生这种情况,便会开始定期进行垃圾回收-它肯定不会像整个程序集一样好清理,但对应用程序的影响最小。


您可以将其推送到此日志中:


 (1) [6.084s][debug][gc,periodic ] Checking for periodic GC. [6.086s][info ][gc ] GC(13) Pause Young (Concurrent Start) (G1 Periodic Collection) 37M->36M(78M) 1.786ms (2) [9.087s][debug][gc,periodic ] Checking for periodic GC. [9.088s][info ][gc ] GC(15) Pause Young (Prepare Mixed) (G1 Periodic Collection) 9M->9M(32M) 0.722ms (3) [12.089s][debug][gc,periodic ] Checking for periodic GC. [12.091s][info ][gc ] GC(16) Pause Young (Mixed) (G1 Periodic Collection) 9M->5M(32M) 1.776ms (4) [15.092s][debug][gc,periodic ] Checking for periodic GC. [15.097s][info ][gc ] GC(17) Pause Young (Mixed) (G1 Periodic Collection) 5M->1M(32M) 4.142ms (5) [18.098s][debug][gc,periodic ] Checking for periodic GC. [18.100s][info ][gc ] GC(18) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 1.685ms (6) [21.101s][debug][gc,periodic ] Checking for periodic GC. [21.102s][info ][gc ] GC(20) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.868ms (7) [24.104s][debug][gc,periodic ] Checking for periodic GC. [24.104s][info ][gc ] GC(22) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.778ms 

想通了吗? 我不知道 在JEP中,日志的每一行都有详细的手语翻译,以及算法的工作方式以及其他所有内容。


“那又怎样,我为什么要找出来?” -你问。 现在,我们有两个附加的句柄: G1PeriodicGCIntervalG1PeriodicGCSystemLoadThreshold ,它们在变坏时可以扭曲。 肯定有一天会很糟糕,是Java,宝贝!



总结


因此,我们手中拥有强大的释放力-不是革命,而是专注于提高性能的发展。 改进的一半实际上与性能有关:三个关于GC的JEP和一个关于CDS的JEP(它们有望自行打开)仅需要升级到JDK12。此外,我们获得了一种语言功能(开关表达式),还有两个针对JDK开发人员的新工具(常量API和JMH测试),现在社区可以更好地关注ARM上的单个64位端口。


通常,现在升级到JDK 12,并且也许Force与您同在。 您将需要它。


分钟的广告。 很快,在4月5日至6日,将举行JPoint会议,该会议将聚集大量对JDK和各种新功能了解很多的人。 例如,肯定会有来自Azul的Simon Ritter的演讲,主题为“ JDK 12:粗心大意的陷阱” 。 最适合讨论最新版本的地方! 您可以在官方网站上了解有关JPoint的更多信息。

Source: https://habr.com/ru/post/zh-CN444434/


All Articles