太多了。所有这些都优化到了字节码级别。

你好呀,我是why。

你好,我是为什么。

太卷了,都优化到字节码层面了

不,不是我。我还是比他年轻帅气。

这就是今天文章的主人公。

他叫布雷特伍尔德里奇,你可能不认识他。

但是我会给你看他的github截图。你一定知道他写的开源项目:

太卷了,都优化到字节码层面了

看到了吗?

太卷了,都优化到字节码层面了

他是著名的伊斯兰之父。

当你看他的github简介时,你会非常激动:

一个降落在地球上的天使的父亲,她不知不觉地进入了我的生活。

照片应该是关于他的孩子的,他在旁边露出了慈祥的笑容,像个老父亲。

文章开头的图片是我从这篇报道中找到的:

问的第一个问题是:

太卷了,都优化到字节码层面了

下面一节,我就从第一人称的角度来告诉你这个兄弟是怎么回答这个问题的。

00-1010什么,你问我为什么写HikariCP?

天啊,不是因为没找到占便宜的家伙。

几年前写代码的时候,我需要一个数据库连接池,所以和大多数开发人员一样,我面对浏览器编程,在互联网上找到了一个开源池。

别告诉我,看起来还不错。

但是后来在对项目做性能测试的时候,慢慢发现池不是很好,总是遇到死锁和连接状态不正确的情况。

我想,这不是一个诡计吗?

太卷了,都优化到字节码层面了

但是当时使用的连接池是开源的。本着开源的精神,我只是想拉下代码,看看是否能帮助修复。

结果,当我打开代码时,好家伙,代码量至少比我预期的多了几千行。

忘记更多代码,你可以耐心阅读。

" data-track="27">神奇的是代码逻辑。

我是去排查死锁问题的,结果我发现锁是一个套一个。

有的时候在一个方法里面获取到锁了,我硬是找不到释放的地方。

最后在隔着十万八千里的地方,看到了释放锁的地方。

我当时大概是这样的:

太卷了,都优化到字节码层面了

因为我知道,我已经没有办法找到死锁潜伏在代码的哪个角落了。

就算我解决了当前的问题,按照项目这样的写法,迟早也会碰到其他的问题。

于是我当机立断,决定...

在网上再找一个。

太卷了,都优化到字节码层面了

这次我学乖了,找到新的连接池之后,我先看了它的代码。

因为被死锁搞怕了,所以特别是关注了关于锁的部分。

新找到的连接池锁的语义确实更清晰了,但是代码量仍然是我预期的二倍多。

除此之外,我研究过的所有的链接池,都在以各种各样的方式违反 JDBC 的合约。

比如,我发现的最常见的一个问题是这样的。

当一个链接被用完了,放回池子里面的时候。某些池子并没有把这个链接里面的消息清理干净,比如自动提交、事务隔离级别等等,导致下个消费者再次拿到这个链接的时候,是一个“脏”链接。

我当时就在想:

太卷了,都优化到字节码层面了

回到最开始的问题。

如上所述,在我写 HikariCP 之前,其实已经有很多成熟的连接池了,那么 HikariCP 是如何变得流行的呢?

再我看来,如果我主打正确性和可靠性,其实不算一个好的卖点,因为我认为这是必须所具备的东西。

所以我专注于推广性能。在我的各个社交媒体上去进行推广。

在 2015 年的某个时候,Wix 工程团队写了一篇关于使用 HikariCP 的博客。

这一波我就直接弹射起飞了。HikariCP 也算是走进了大家的视野。

最后,我确实希望随着时间的推移,更多的用户会对正确性和可靠性给予同等的重视,没有这些性能就没有意义。

就我而言,我打算多写一些关于 HikariCP 的这些方面的文章。

性能为啥牛逼?

前面说了,HikariCP 的卖点是强悍的性能。

那么它的性能为啥这么牛逼呢?

其实答案就写在 HikariCP 的 github 主页上:

太卷了,都优化到字节码层面了

在进入 how we do it here 之前,先简单说说这个项目的名称。

可以看到一个大大汉字:光。

关于这个名字的来源,其实在前面提到的报道中也提到过:

太卷了,都优化到字节码层面了

HikariCP,被翻译为“光”,在英语中,在HikariCP的上下文中,它是一个双关语。在这个项目里面,"光"不只表示速度快,也是指代码量很少。

Hikari 的发音是 Hi-ka-lee。

这个大家记一下。

我记得有一次面试,有个面试者提到了这个连接池,但是他不知道怎么读。

他说:就是 H 开通 CP 结尾的那个连接池,咋读的我忘记了。

但是我当时也一下就反应过来了。

我说:嗯,我知道你说的哪个连接池,你继续说。

其实,当时我也不知道怎么读,就很尴尬。

好了,接下来,就一起看看性能为啥这么牛逼。

答案,作者都在 github 里面写着呢:

首先,这个文章的标题就很有意思:

太卷了,都优化到字节码层面了

wath mean is Down the Rabbit Hole?

直译过来是“在兔子洞里”。

太卷了,都优化到字节码层面了

我觉事情没有这么简单,于是我去查了一下:

太卷了,都优化到字节码层面了

哦,down the rabbit hole 原来是是冒险进入未知世界的隐喻。出自著名的《爱丽丝梦游仙境》一书中。

一般我们用 down the rabbit hole 用来描述陷入一个愈发奇怪、令人摸不着头脑或出人意料的状况,而且一件事情促使另一件事情的发生,接连不断,因此越陷越深、无从脱身的场景。

一个英语的小俚语,送给大家。

太卷了,都优化到字节码层面了

知道标题的含义后,等你看完作者写的文章之后,你再次审视这个“兔子洞”的标题,你就会发现:真特么贴切啊。

全文读完,理解之后,我发现作者想表达的为什么这么快的原因有四个:

  • 字节码级别的优化-尽量的利用 JIT 的内联手段
  • 字节码级别的优化-利用更容易被 JVM 优化的指令
  • 代码级别的优化-利用改造后的 FastList 代替 ArrayList
  • 代码级别的优化-利用无锁的 ConcurrentBag

我们一个个的看。

字节码级别的优化

文章的开头,作者就说了:我这波操作在字节码,就问你牛不牛逼。

太卷了,都优化到字节码层面了

简单的翻译一下关键的地方:

这个地方作者提到了 JIT 的内联优化。

啥是内联?

内联其实是一个动作。

举个简单的例子,假设代码是这样的:

int result = add(a,b);private int add(int x,int y){    return x+y;}

那么经过 JIT 的内联优化之后,代码就会变成这样:

int result= a + b;

这样,节约了调用 add 方法的开销。

内联,也被称为优化子母,它为其他的优化手段建立了非常好的基础,所以除了上面写的那个例子之外,还有很多的更加进阶的体现方式,比如逃逸分析、循环展开、锁消除:

太卷了,都优化到字节码层面了

那么一个调用的开销到底有哪些呢?

我想无外乎就是这几步:

  • 首先要设置方法调用需要传递的参数,对吧?
  • 有了参数,是不是还得查询具体调用哪个方法,对吧?
  • 然后如果有类似于局部变量,或者求值这样的方法,还得创建新的调用栈帧,创建新的运行时数据结构,对吧?
  • 最后,还有可能需要给调用方返回一个结果,对吧?

有的朋友就就会说了,至于吗?这个开销看着也不大啊?

是的,确实不大,但是当再小的一个优化点,乘以一个巨大的调用量之后,最终的结果都是很可观的。

我想这个道理大家都明白。

作者也在文章里面说了:

太卷了,都优化到字节码层面了

HikariCP 包含了许多微观的优化,这些优化单独来看几乎无法衡量,但结合起来就能提升整体性能。

甚至在数百万次的调用中,优化的级别是以毫秒的时间来衡量的。

可能这就是大佬吧。

我想,追求性能的极致,也就不过如此了。

接下来,说说另外一个字节码级别的优化:

invokevirtual vs invokestatic

这波优化我觉得简直就是在大气层了。

作者举了个例子。

之前获取 Connection, Statement, ResultSet 的代理对象什么的都是通过单例工厂方法。

就类似于这样的:

太卷了,都优化到字节码层面了

ROXY_FACTORY 就是一个 static 的字段。

上面的代码的字节码大概是这样的:

太卷了,都优化到字节码层面了

通过字节码你可以看到,首先有一个 getstatic 调用,来获得静态字段 PROXY_FACTORY 的值。

还有一个 invokevirtual 指令的调用,对应的就是 ProxyFactory 实例的 getProxyPreparedStatement() 方法:

15: invokevirtual #69  // Method com/zaxxer/hikari/proxy/ProxyFactory.getProxyPreparedStatement:(Lcom/zaxxer/hikari/proxy/ConnectionProxy;Ljava/sql/PreparedStatement;)Ljava/sql/PreparedStatement;

这个地方有什么优化空间呢?

作者把代码修改成了这样:

太卷了,都优化到字节码层面了

其中 ProxyFactory 是通过 Javassist 生成的。

所以你去看 ProxyFactory 源码,全是空实现,

太卷了,都优化到字节码层面了

它真正的实现逻辑,是对应源代码的这个类,就不具体展示了,有兴趣的可以下来看看:

然后,把 getProxyPreparedStatement 方法做成了 static。

然后字节码就变成了这样:

太卷了,都优化到字节码层面了

神奇的事情就发生了:

太卷了,都优化到字节码层面了

  • getstatic 指令消失了
  • invokevirtual 被替换成了 invokestatic 调用,这样更加容易被JVM优化。
  • 最后,可能第一眼没有注意到的是,堆栈大小从 5 减少到 4 。这是因为在 invokevirtual 的情况下,ProxyFactory 的实例被隐含地传递到了堆栈中(也就是 this 对象),而且在调用 getProxyPreparedStatement() 时,还有一个额外的从堆栈中弹出的操作。

第 1,3 点应该问题不大。大家都能明白是怎么回事。

但是这个第二点:invokevirtual 被替换成了 invokestatic 调用,这样更加容易被JVM优化。

说真的,我第一次看到的时候大概是这样的:

太卷了,都优化到字节码层面了

为啥啊?

invokevirtual 和 invokestatic 是干啥的我倒是还记得。

但是 invokestatic 的性能会更好一点吗?

于是我带着这个问题去翻了《深入理解JVM虚拟机》,没有直接找到答案。

但是还是有意外收获的。就是写下了这篇文章:《报告!书里有个BUG》

不然你觉得我为什么会突然翻到书里面的这一部分,都是有契机的。

太卷了,都优化到字节码层面了

虽然,书里面没有直接把答案写出来,但是在相关部分有这样的一段话:

太卷了,都优化到字节码层面了

我理解一下就是 invokevirtual 指令,需要查询虚方法表才能确定方法的直接引用。

而 invokestatic 在类加载的时候,就可以从符号引用转成直接引用。

这样看来,invokestatic 确实是优于 invokevirtual 的。

那么问题又来了。

类加载的过程是什么?

invokestatic 是在哪个过程搞事情的?

肯定是解析阶段哈,朋友们。

解析阶段,就是 JVM 将常量池内的符号引用替换为直接引用的过程。

扯远了,说回来。

上面只是我的一点猜测,我相信肯定不止我一个人看了作者的“兔子洞”文章后关于 invokevirtual vs invokestatic 这一块有疑问。

于是,我去查了一圈。

果不其特么的然。(抱歉爆粗了,但是我确实找了很久。)

找到了这个链接,链接的前半部分和我的问题一模一样:

太卷了,都优化到字节码层面了

作者的回复如下:

太卷了,都优化到字节码层面了

后面那一段 Additionally 很好理解。

就是前面说的,静态调用少一个堆栈,在运行时就少一个推/拉操作,这进一步提高了性能。

主要是前面这段,有亿点点难懂。

他说:简而言之,JVM 在做内联调用的时候,即使是单态的内联,它也必须安装一个 trap(陷阱),以防另一个实现出现,并将调用转变为多态。

这个 trap 的设置和清除给调用增加了一点开销。

怎么样,懵不懵逼?

其实,他这句话,我个人理解,说的就是 Java 的动态分派,聊的就是 JVM 的 CHA(Class Hierarchy Analysis,类型继承关系分析) 技术。

答案就写在《深入理解Java虚拟机(第三版)》的 417 页,翻去吧:

太卷了,都优化到字节码层面了

你非要问我证据是什么,那么这两个单词呼应上了,你说这事多巧?

太卷了,都优化到字节码层面了

invokevirtual 调用的是虚方法,按照书里的说法,前面提到的 trap 其实就是这里的“逃生门”:

太卷了,都优化到字节码层面了

而 This trap setting and clearing adds slightly more overhead to the invocation(这个 trap 的设置和清除给调用增加了一点开销)这句话,其实就是对应这里:

太卷了,都优化到字节码层面了

现在你知道为什么对于 JVM 来说, invokestatic 比 invokevirtual 更容易优化了吧?

优化指的就是内联。

invokestatic 调用的是静态方法,对于非虚方法,JVM 可以直接进行内联,这种内联是有百分之百的安全保障的。

而 invokevirtual 调用的是虚方法,对于虚方法的内联,就得上 CHA 机制,设置逃生门这一套玩意。

虽然都是内联,这不得多消耗一点性能嘛。

内联已经是性能优化了,让代码更好的内联,优化性能优化的优化。

这波操作,在大气层。

太卷了,都优化到字节码层面了

好了,上面就是字节码层面的优化了,接着我们看代码层面的优化。

代码层面的优化

代码层面上最出名的就是 FastList 替换 ArrayList 这个玩意了。

太卷了,都优化到字节码层面了

首先,我去看了项目的提交记录,在 2014 年 1 月 15 日的时候,作者进行了一次提交:

太卷了,都优化到字节码层面了

备注的后半部分我们应该很熟悉了,前面已经讲过了。

前面就是用 FastList 替换 ArrayList 的那一次提交。

Java ArrayList 在每次调用 get(int index) 时都会进行范围检查。在 HikariCP 项目中,可以保证 index 在正确的范围内,所以这个检查没有意义,于是就去掉了:

太卷了,都优化到字节码层面了

再比如,ArrayList 的 remove(Object o) 方法是从头扫到尾。

假设要删除最后一个元素,需要遍历整个数组。

巧就巧在,比如 HikariCP 的 Statement,按照我们的编码习惯,删除(关闭)应该先删除最后一个。

所以 FastList 优化了 remove(Object element) 方法, 将查找顺序变成了逆序查找:

太卷了,都优化到字节码层面了

整体来看,FastList 的优化点就上面说的 get 和 remove 方法。

接着,看看另外一个代码级别的优化:

太卷了,都优化到字节码层面了

作者列了几个点:

  • 一个无锁的设计
  • 线程本地缓存
  • 窃取队列
  • 直接交接的优化

作者介绍的很简单,其实这里面还是很有东西的。

一个重要的技巧是 ConcurrentBag 通过 ThreadLocal 做了一次连接的预分配。

通过 ThreadLocal 一定程度上避免了共享资源的竞争。

自己看代码的话主要看看 add(空闲连接加入队列)、borrow(获取连接)、requite(释放连接) 方法。

网上也有很多相应的文章去介绍,有兴趣的可以去了解一下,我这就不写了。

哦,你不想看其他的文章,就想等着我给你讲呢? 好的,先欠着,欠着。 偷个懒,文章写太长了也没人看。

太卷了,都优化到字节码层面了

打起来了

在写文章的过程中,我还看到了这样的一个 issue,感觉有点意思,写一下。

太卷了,都优化到字节码层面了

一个小哥说:

HikariCP 的作者很快就进行了回复:

太卷了,都优化到字节码层面了

这就有点意思了。

虽然我不会说这是"作弊",这话说的,就像是:有一句话我不知当讲不当讲。

然后就接着讲出来了。

接着,另外一个吃瓜网友说:

太卷了,都优化到字节码层面了

太卷了,都优化到字节码层面了

HikariCP 的作者也表示这句话没毛病,但是他强调了自己的 HikariCP 也是给监控留了口子的:

上面的对话,都是发生在 2015 年 1 月。

但是一年半后, 2016 年 7 月 26 日,这个问题又被一个人激活了:

太卷了,都优化到字节码层面了

wenshao,来者何人?

太卷了,都优化到字节码层面了

此人正是 druid 的爸爸之一,江湖人称温少。

也许你不认识温少,也许你不知道温少写的 druid,但是你一定知道温少的另外一个大作:

太卷了,都优化到字节码层面了

问题是多了一点,但是并不妨碍别人是大神。

可以直接端茶:

太卷了,都优化到字节码层面了

首先温少说:如果你配置了maxWait 属性,druid 会使用公平锁,所以降低了性能。

至于为什么这样的,是因为在生产环境中遇到的一些问题,设计如此。

然后他接着提到了淘宝:

太卷了,都优化到字节码层面了

链接点进去,标题是这样的:

太卷了,都优化到字节码层面了

说的是 2015 年的天猫双 11。

标题翻译过来就是:

我还在链接里面看到了好久没见的马爸爸:

太卷了,都优化到字节码层面了

我理解温少放这个链接的意思就是说,druid 在阿里内部使用,天猫双十一是一个非常牛逼的场景,druid 经受住了这样场景的考验。

HikariCP 的作者并没有回复温少。

直到另外一个吃瓜群众的推波助澜:

太卷了,都优化到字节码层面了

HikariCP 的作者寻思,这是要进行数据量的 battle 了呀。

那我就不客气了。

太卷了,都优化到字节码层面了

HikariCP 是世界上使用最广泛的连接池之一,被一些最大的公司使用,每天为数十亿的用户提供服务。

而对于 Druid,不好意思,我说话有点直:在中国以外的地方很少见。

但是对于他的这个回答,很快就有人提出了质疑:

太卷了,都优化到字节码层面了

要数据是吧?坐稳了:

太卷了,都优化到字节码层面了

  • wix.com托管着超过1.09亿个网站,每天处理的请求超过10亿个。
  • Atlassian的产品拥有数百万的客户。
  • HikariCP是 spring boot 的默认连接池。
  • HikariCP每月从中央maven仓库解析超过30万次。

这些公司都在用:

太卷了,都优化到字节码层面了

这个回答之后,双方都没有说话了。

两方之间的 battle 就算是结束了。

但是还有人在继续跟帖,我觉得这个哥们属于清醒吃瓜:

太卷了,都优化到字节码层面了

另外一个老哥的回答就有意思了:

太卷了,都优化到字节码层面了

而我觉得,这场 battle 其实真的没有特别大的意义。

在技术选型上,没有最好的,只有合适的。

Druid 和 HikariCP 各有各的优势。

最后说一句(求关注)

好了,看到了这里点个关注吧,周更很累的,需要一点正反馈。

感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/50920.html

(0)

相关推荐

  • 人的智商遗传母亲还是父亲,儿女智商遗传老妈还是老爸

    自古以来,每个人身上都依稀可以看到父母的模样。比如演员戚薇和她的女儿,不光眉眼相似,就连举止神情也像是一个模子刻出来的,网友们纷纷感慨:戚薇简直是生了个自己啊!

    生活 2021年11月16日
  • 颠覆常识!高盐饮食能抑制肿瘤生长吗?吃多少盐才健康?

    长期高盐饮食,会引发健康隐患,几乎已经成为人尽皆知的常识。所以,在各种各样的饮食建议中,都推荐清淡饮食,呼吁大家限制盐分的摄入。

    生活 2021年12月8日
  • 中国石油定价机制,石油价格谁控制

    昨天白宫发布声明,美国将释放5000万桶石油的战略储备,与中国、印度、日本、韩国和英国联手降低油价。

    生活 2021年11月25日
  • 军用风电、新能源汽车、核电,2天上涨14.96%!我还能进去吗?

    一、热门个股解读——佳电股份(000922)

    生活 2021年11月7日
  • 豆瓣被下架;交警现场查酒驾“翻车”,官方回应;证券公司员工因替客户考研被捕|孝南早报

    编辑 | 杨文瑾豆瓣被下架据微信公众号“工信微报”,工业和信息化部信息通信管理局12月9日发布“关于下架侵害用户权益APP名单的通报”,共下架106款APP,包括豆瓣、唱吧、爱回收、看看新闻、妈妈网孕育等。通报称:今年以来,工信部持续推进APP侵害用户权益专项整治行动,加大常态化检查力度,先后三次组织对用户反映强烈的重点问题开展“回头看”。11月3日,工信部针对APP超范围、高频次索取权限,非服务场景所必需收集用户个人信息,欺骗误导用户下载等违规行为进行了检查,并对未按要求完成整改的APP进行了公开通报。截至目前,尚有5款APP未按工信部要求完成整改。各通信管理局按照工信部统筹部署,积极开展APP技术检测,截至目前尚有101款APP仍未完成整改。依据《个人信息保护法》《网络安全法》等相关法律要求,工信部组织对上述共计106款APP进行下架,相关应用商店应在本通报发布后,立即组织对名单中应用软件进行下架处理。针对部分违规情节严重、拒不整改的APP,属地通信管理局应对APP运营主体依法予以行政处罚。(来源:工信微报)不止APP,很多小程序不给读取位置和个人信息就不能用。孙卓户籍地为何在黑龙江?“养母”这么说...12月9日,据山东阳谷县公安局通报,专案组已对被拐孩子孙卓、符建涛的户口办理问题展开调查。警方初步查明,孙海洋之子孙卓现用姓名国某,户籍地在黑龙江省某市。符建涛现用姓名为吴某某,户籍地在阳谷县。孙卓养母表示,因当年计划生育山东生三胎罚款严重,便让父亲帮忙把孙卓户口安在黑龙江。孙海洋在接受采访时表示,被拐儿童户口也是其最关注的问题,他暂未与养父母见面,应由法律去教育他们。(来源:新京报)高考要去黑龙江考了?明确了!职业本科与普通本科学位证书有同样效力国务院学位委员会办公室日前印发《关于做好本科层次职业学校学士学位授权与授予工作意见》。意见明确了职业本科学士学位授权、授予等的政策依据及工作范围,对职业本科学士学位授予权的审批权限和申请基本条件及授予方式、基本程序、授予标准、授予类型、学士学位证书和学位授予信息提出了要求。在执行方面,普通本科和职业本科都按照《中华人民共和国学位条例》《中华人民共和国学位条例暂行实施办法》《学士学位授权和授予管理办法》进行学士学位授权、授予、管理和质量监督;在证书效用方面,两者价值等同,在就业、考研、考公等方面具有同样的效力。(来源:教育部网站)好消息!唐山交警直播查酒驾“翻车”?官方通报:涉事民警和辅警已停职近日,有网友称河北唐山曹妃甸区交警直播查酒驾时“翻车”了。一名司机两次吹气测试,数值从“103”降至“45”,之后交警中断了直播。网传视频显示,此事发生在12月6日晚上。当时,曹妃甸交警正在道路上设卡查酒驾,“曹妃甸交警”抖音号同时进行直播。网友称,交警让一名驾驶员吹气,第一次吹出的数值为“103”,再次吹气后,数值显示“45”,随后直播被关了。12月9日下午,曹妃甸警方发布通报称,涉事民警和辅警已停职接受调查。根据相关规定,驾驶人员每100毫升血液酒精含量大于或等于20毫克小于80毫克为饮酒后驾车;每100毫升血液酒精含量大于或等于80毫克为醉酒驾车。因此酒精测试中,“103”和“45”,两个不同的数字,处罚截然不同。(来源:曹妃甸公安、极目新闻)103降到45,吹一下还能解酒?证券公司员工替客户考研被抓12月9日,“证券公司员工代替客户考研被抓”的消息登上热搜。该员工是中信证券大连分公司员工,为了年底冲业绩,铤而走险代替客户进行考研。12月8日,中国检察网公布一份代替考试罪起诉书。起诉书内容显示,李某是中信证券公司大连分公司员工,研究生学历。2020年12月26日,李某代替兴某(客户)考试时被考务室监考老师发现并带至凌河公安分局派出所执勤民警处。经核实李某代替考试的情况属实,将李某当场抓获。经法院审理查明,兴某报考参加2020年全国研究生招生考试,因害怕考试不通过,找到李某要求其代替自己参加考试。兴某了解到李某是证券行业新人,客户少、销售业绩不佳,就对李某承诺,如果其同意代替被告人兴某参加考试,就给其拉客户购买证券,以提高李某的年底销售业绩。2021年1月15日兴某投案自首。被告人李某和兴某,均因涉嫌代替考试罪被锦州市公安凌河分局取保候审。李某为拉业务代替客户考试的消息迅速在网络流传,网友在看到二人涉嫌违法的同时,也不禁感叹证券公司员工为了考核这么“拼”吗?不少网友都惊讶问道,“金融行业已经卷成这样了吗?”(来源:新京报)自由诚可贵,业绩价更高。

    科技 2021年12月11日