从源码里的一个注释,我追溯到了12年前,有点意思。 (下)

简介: 从源码里的一个注释,我追溯到了12年前,有点意思。 (下)

继续挖


继续往下挖的线索,其实已经在前面出现过了:

image.png

通过这个链接,我们可以来到这个地方:

https://stackoverflow.com/questions/2785964/in-arrayblockingqueue-why-copy-final-member-field-into-local-final-variable

image.png

瞟一眼我框起来的代码,你会发现这里抛出的问题其实又是和前面是一样。

我为什么又要把它拿出来说一次呢?

因为它只是一个跳板而已,我想引出这下面的一个回答:

image.png

这个回答说里面有两个吸引到我注意的地方。

第一个就是这个回答本身,他说:这是该类的作者 Doug Lea 喜欢使用的一种极端优化。这里有个超链接,你可以去看看,能很好地回答你的问题。

这里面提到的这个超链接,很有故事:

http://mail.openjdk.java.net/pipermail/core-libs-dev/2010-May/004165.html

但是在说这个故事之前,我想先说说这个回答下面的评论,也就是我框起来的部分。

这个评论观点鲜明的说:需要着重强调“极端”!这不是每个人都应该效仿的、通用的、良好的写法。

凭借我在 stackoverflow 混了这么几年的自觉,这里藏龙卧虎,一般来说 说话底气这么足的,都是大佬。

于是我点了他的名字,去看了一眼,果然是大佬:

image.png

这哥们是谷歌的,参与了很多项目,其中就有我们非常熟悉的 Guava,而且不是普通开发者,而是 lead developer。同时也参与了 Google 的 Java 风格指南编写。

所以他说的话还是很有分量的,得听。

然后,我们去到那个很有故事的超链接。

这个超链接里面是一个叫做 Ulf Zibis 的哥们提出的问题:

image.png

Ulf 同学的提问里面提到说:在 String 类中,我经常看到成员变量被复制到局部变量。我在想,为什么要做这样的缓存呢,就这么不信任 JVM 吗,有没有人能帮我解答一下?

Ulf 同学的问题和我们文章中的问题也是一样的,而他这个问题提出的时间是 2010 年,应该是我能找到的关于这个问题最早出现的地方。

所以你要记住,下面的这些邮件中的对话,已经是距今 12 年前的对话了。

在对话中,针对这个问题,有比较官方的回答:

image.png

来自 SUN 公司的 JDK 并发大师,就问你怕不怕。

他说:这是一种由 Doug Lea 发起的编码风格。这是一种极端的优化,可能没有必要。你可以期待 JIT 做出同样的优化。但是,对于这类非常底层的代码来说,写出的代码更接近于机器码也是一件很 nice 的事情。

关于这个问题,这几个人有来有回的讨论了几个回合:

image.png

主要再看看这个叫做 Osvaldo 对线 Martin 的邮件:

https://mail.openjdk.java.net/pipermail/core-libs-dev/2010-May/004168.html


微信图片_20220429091212.png


Osvaldo 老哥写了这么多内容,主要是想喷 Martin 的这句话:这是一种极端的优化,可能没有必要。你可以期待 JIT 做出同样的优化。

他说他做了实验,得出的结论是这个优化对以 Server 模式运行的 Hotspot 来说没有什么区别,但对于 Client 模式运行的 Hotspot 来说却非常重要。在他的测试案例中,这种写法带来了 6% 的性能提升。

然后他说他现在包括未来几年写的代码应该都会运行在以 Client 模式运行的 Hotspot 中。所以请不要乱动 Doug 特意写的这种优化代码,我谢谢你全家。

同时他还提到了 JavaME、JavaFX Mobile&TV,让我不得不再次提醒你:这段对话发生在 12 年前,他提到的这些技术,在我的眼里已经是过眼云烟了,只听过,没见过。

哦,也不能算没见过,毕竟当年读初中的时候还玩过 JavaME 写的游戏。

就在 Osvaldo 老哥言辞比较激烈的情况下,Martin 还是做出了积极的回应:

image.png

Martin 说谢谢你的测试,我也已经把这种编码风格融合到我的代码里面了,但是我一直在纠结的事情是是否也要推动大家这样去做。因为我觉得我们可以在 JIT 层面优化这个事情。

接下来,最后一封邮件,来自一位叫做 David Holmes 的老哥。

巧了,这位老哥的名字在《Java并发编程实战》一书里面,也可以找到。

人家就是作者,我介绍他的意思就是想表达他的话也是很有分量的:

image.png

因为他的这一封邮件,算是给这个问题做了一个最终的回答。

image.png

我带着自己的理解,用我话来给你全文翻译一下,他是这样说的:

我已经把这个问题转给了 hotspot-compiler-dev,让他们来跟进一下。

我知道当时 Doug 这样写的原因是因为当时的编译器并没有相应的优化,所以他这样写了一下,帮助编译器进行优化了一波。但是,我认为这个问题至少在 C2 阶段早就已经解决了。如果是 C1 没有解决这个问题的话,我觉得是需要解决一下的。

最后针对这种写法,我的建议是:在 Java 层面上不应该按照这样的方式去敲代码。

There should not be a need to code this way at the Java-level.

至此,问题就梳理的很清楚了。

首先结论是不建议使用这样的写法。

其次,Doug 当年这样写确实是一种优化,但是随着编译器的发展,这种优化下沉到编译器层面了,它帮我们做了。

最后,如果你不明白前面提到的 C1,C2 的话,那我换个说法。

C1 其实就是 Client Compiler,即客户端编译器,特点是编译时间较短但输出代码优化程度较低。

C2 其实就是 Server Compiler,即服务端编译器,特点是编译耗时长但输出代码优化质量也更高。

前面那个 Osvaldo 说他主要是用客户端编译器,也就是 C1。所以后面的 David Holmes 才一直在说 C2 是优化了这个问题的,C1 如果没有的话可以跟进一下,巴拉巴拉巴拉的...

关于 C2 的话,简单提一下,记得住就记,记不住也没关系,这玩意一般面试也不考。

大家常常提到的 JVM 帮我们做的很多“激进”的为了提升性能的优化,比如内联、快慢速路径分析、窥孔优化,都是 C2 搞的事情。

另外在 JDK 10 的时候呢,又推出了 Graal 编译器,其目的是为了替代 C2。

至于为什么要替换 C2,额,原因之一你可以看这个链接...

http://icyfenix.cn/tricks/2020/graalvm/graal-compiler.html

C2 的历史已经非常长了,可以追溯到 Cliff Click 大神读博士期间的作品,这个由 C++ 写成的编译器尽管目前依然效果拔群,但已经复杂到连 Cliff Click 本人都不愿意继续维护的程度。

你看前面我说的 C1、C1 的特点,刚好是互补的。

所以为了在程序启动、响应速度和程序运行效率之间找到一个平衡点,在 JDK 6 之后,JVM 又支持了一种叫做分层编译的模式。

也是为什么大家会说:“Java 代码运行起来会越来越快、Java 代码需要预热”的根本原因和理论支撑。

在这里,我引用《深入理解Java虚拟机HotSpot》一书中 7.2.1 小节[分层编译]的内容,让大家简单了解一下这是个啥玩意。

首先,我们可以使用 -XX:+TieredCompilation 开启分层编译,它额外引入了四个编译层级。

  • 第 0 级:解释执行。
  • 第 1 级:C1 编译,开启所有优化(不带 Profiling)。Profiling 即剖析。
  • 第 2 级:C1 编译,带调用计数和回边计数的 Profiling 信息(受限 Profiling).
  • 第 3 级:C1 编译,带所有Profiling信息(完全Profiling).
  • 第 4 级:C2 编译。

常见的分层编译层级转换路径如下图所示:


image.png

  • 0→3→4:常见层级转换。用 C1 完全编译,如果后续方法执行足够频繁再转入 4 级。
  • 0→2→3→4:C2 编译器繁忙。先以 2 级快速编译,等收集到足够的 Profiling 信息后再转为3级,最终当 C2 不再繁忙时再转到 4 级。
  • 0→3→1/0→2→1:2/3级编译后因为方法不太重要转为 1 级。如果 C2 无法编译也会转到 1 级。
  • 0→(3→2)→4:C1 编译器繁忙,编译任务既可以等待 C1 也可以快速转到 2 级,然后由 2 级转向 4 级。

如果你之前不知道分层编译这回事,没关系,现在有这样的一个概念就行了。

再说一次,面试不会考的,放心。

好了,恭喜你看到这里了。回想全文,你学到了什么东西呢?

是的,除了一个没啥卵用的知识点外,什么都没有学到。

image.png

目录
相关文章
|
1天前
|
安全 Java 编译器
一个 Bug JDK 居然改了十年?
你敢相信么一个简单的Bug,JDK 居然花了十年时间才修改完成。赶快来看看到底是个什么样的 Bug?
9 1
一个 Bug JDK 居然改了十年?
|
7月前
|
XML 程序员 C#
C注释的高级使用技巧,让你的代码无敌了!
C注释的高级使用技巧,让你的代码无敌了!
35 0
|
7月前
|
Java
注释之背后:代码的解释者与保护者
注释之背后:代码的解释者与保护者
39 0
自 创 日 历 (在代码里有注释讲细节)
做日历主要是要确定好第一天是星期几,然后算间隔多少天,算出具体这一天是星期几,然后把我们想打印的打印出来,把每个月的第一天定位到该在的地方。
78 0
|
Java
【‘’注释‘’】哇哦,这是心动的感觉
【‘’注释‘’】哇哦,这是心动的感觉
83 0
【‘’注释‘’】哇哦,这是心动的感觉
|
Shell
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(二)
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(二)
226 0
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(二)
|
运维 Devops Linux
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(一)
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(一)
207 0
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(一)
|
Shell
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(三)
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(三)
254 0
Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)(三)
|
程序员
代码界10个最“牛叉”的代码注释
要说到代码注释这个东西吧,其实很神奇,因为不管写不写注释,其实对于代码的运行没有任何的影响,注释的长短也没关系,因为编译器会对于所有的代码注释都是过滤掉的,其实注释非常重要,对后期的代码维护和重构至关重要,但是其实很多程序员童鞋在写代码时往往并不注意注释,所以导致自己回头看自己的代码时也都忘了写的是什么,本文给出了 StackOverflow 网友针对“你看到过的最好的代码注释是什么样的?”这个问题给出的回答的前10条。
6283 0
|
安全 Java 程序员
从源码里的一个注释,我追溯到了12年前,有点意思。 (中)
从源码里的一个注释,我追溯到了12年前,有点意思。 (中)
126 0
从源码里的一个注释,我追溯到了12年前,有点意思。 (中)