<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從HikariCP的性能優(yōu)化說起!

          共 7316字,需瀏覽 15分鐘

           ·

          2021-08-18 17:39

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進(jìn)!你不來,我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!

          編輯:業(yè)余草

          推薦:https://www.xttblog.com/?p=5265

          你好,我是業(yè)余草,這是我的第 445 篇原創(chuàng)文章。

          這篇文章我想了很久沒想到好標(biāo)題,索性就以《從HikariCP的性能優(yōu)化說起!》為題開始吧!

          這兩天看到群里有人閱讀到網(wǎng)上的文章,在群里問:“invokestatic 性能比 invokevirtual 好?”

          一時(shí)間難倒了不少人,有人建議去看周老師的 JVM 書籍(深入理解Java虛擬機(jī))中找答案,引起了群友廣泛的討論。不少人表示沒看過,還有部分表示看不懂。

          其實(shí)不看周老師的書,也能搞定這個(gè)問題。

          同時(shí),阿里巴巴出品的 Java 開發(fā)手冊(cè)中也有一段這樣的描述:

          ?

          【強(qiáng)制】避免通過一個(gè)類的對(duì)象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成本,直接用類名來訪問即可。

          ?

          這句話說的太模糊了,也不給一個(gè)詳細(xì)的解釋,看的云里霧里。很多人都看懵了,也包括我自己。沒得辦法,我嘗試著從字節(jié)碼的角度找找答案,結(jié)果還真被我找到了。

          我們先來看一個(gè) demo:

          public class Xttblog {
              public static int num = 0;

              public void add(){
                  this.num ++;
              }
          }

          這段代碼,如果你安裝了 p3c 插件,就會(huì)有爆紅提示:

          不應(yīng)該通過類實(shí)例訪問靜態(tài)成員

          我們先忽略這個(gè)提示,寫個(gè) main 方法,看看字節(jié)碼層面指令。

          public class StaticTest {
              public static void main(String[] args) {
                  Xttblog xttblog = new Xttblog();
                  xttblog.num ++;
              }
          }

          通過類實(shí)例訪問靜態(tài)變量,阿里巴巴的規(guī)約插件同樣的會(huì)報(bào)出紅線提示。

          阿里巴巴Java開發(fā)規(guī)約插件p3c提示

          這個(gè)提示不影響代碼運(yùn)行。我們可以執(zhí)行javap,不會(huì)用的先看java -help,或者 idea 安裝字節(jié)碼插件:jclasslib。

          Compiled from "StaticTest.java"
          public class com.xttblog.test.StaticTest {
            public static void main(java.lang.String[]);
              descriptor: ([Ljava/lang/String;)V
              flags: ACC_PUBLIC, ACC_STATIC
              Code:
                stack=2, locals=2, args_size=1
                   0new           #2                  // class com/xttblog/Xttblog
                   3: dup
                   4: invokespecial #3                  // Method com/xttblog/Xttblog."<init>":()V
                   7: astore_1
                   8: aload_1
                   9: pop
                  10: getstatic     #4                  // Field com/xttblog/Xttblog.num:I
                  13: iconst_1
                  14: iadd
                  15: putstatic     #4                  // Field com/xttblog/Xttblog.num:I
                  18return
                        LineNumberTable:
                  line 110
                  line 128
                  line 1318
                LocalVariableTable:
                  Start  Length  Slot  Name   Signature
                      0      19     0  args   [Ljava/lang/String;
                      8      11     1 xttblog   Lcom/xttblog/Xttblog;
          }

          我們?cè)俅胃乱幌?main 方法中的代碼:

          public class StaticTest {
              public static void main(String[] args) {
                  /*Xttblog xttblog = new Xttblog();
                  xttblog.num++; */


                  Xttblog.num ++;
              }
          }

          再一次的查看字節(jié)碼。

          Compiled from "StaticTest.java"
          public class com.xttblog.test.StaticTest {
            public com.xttblog.test.StaticTest();
              Code:
                 0: aload_0
                 1: invokespecial #1                  // Method java/lang/Object."<init>":()V
                 4return

            public static void main(java.lang.String[]);
              Code:
                 0: getstatic     #2                  // Field com/xttblog/test/Xttblog.num:I
                 3: iconst_1
                 4: iadd
                 5: putstatic     #2                  // Field com/xttblog/test/Xttblog.num:I
                 8return

          你會(huì)發(fā)現(xiàn)指令變少了。除了少了創(chuàng)建對(duì)象的 new、dup、invokespecial 指令外,還多出了 astore_1、aload_1、pop 指令。

          astore_1 指令:1 是代表在本地變量表(LocalVariableTable)中的序號(hào)位置。Slot 1 就是 xttblog 對(duì)象。它的意思就是,創(chuàng)建完對(duì)象后,把 xttblog 存入到本地變量表位置 1 中。

          aload_1 指令:把存放在局部變量表中索引 1 位置的對(duì)象引用壓入操作棧。

          pop 指令:然后,pop 指令,又把棧頂給彈出了。

          到現(xiàn)在,你應(yīng)該看明白了。通過類實(shí)例去訪問靜態(tài)變量,來來回回的把 xttblog 對(duì)象給折騰了一遍。它多出了,存入本地變量表,壓棧,出棧的操作。把本來利索的事情,給多加幾道工序,反而影響運(yùn)行效率。

          其他的指令我就不講了,具體可以查看我前面的文章:《JVM 常用指令速查手冊(cè),建議收藏!》。

          JVM部分指令

          總結(jié)一下,就是靜態(tài)變量或方法的訪問比通過類實(shí)例訪問靜態(tài)變量和方法快。同樣的道理,invokestatic 性能比 invokevirtual 好,也就不足為奇了。

          實(shí)際上,HikariCP 也利用了 Javassist 來生成委托實(shí)現(xiàn)動(dòng)態(tài)代理,優(yōu)化并精簡了字節(jié)碼。

          invokevirtual vs invokestatic

          具體官方英文文檔,參考這里:https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole

          HikariCP 的作者,也就是 Brett Wooldridge 大神也是通過字節(jié)碼來說明,invokestatic 性能比 invokevirtual 好,同時(shí) invokestatic 相比 invokevirtual 更容易被 JVM 優(yōu)化調(diào)用。

          HikariCP作者的性能優(yōu)化

          另外,優(yōu)化后的(上圖中的上半圖)指令,還少了一個(gè) getstatic。同時(shí)棧大小從 5 個(gè)元素減少到 4 個(gè)元素。這是因?yàn)?invokevirtual 指令在堆棧上隱式傳遞 ProxyFactory 實(shí)例的情況下(即 this),并且在調(diào)用時(shí)從堆棧中需要額外(看不見)彈出 this。

          讀過周志明老師的《深入理解Java虛擬機(jī)》第二版的同學(xué),可能還知道:invokevirtual 指令的調(diào)用依賴于運(yùn)行時(shí)解析,其解析過程大致分為以下幾個(gè)步驟:

          • 找到操作數(shù)棧頂?shù)牡谝粋€(gè)元素(本例中就是 this)所指向的對(duì)象的實(shí)際類型,記作 C。
          • 如果在類型 C 中找到了與常量中的描述符和簡單名稱都相符的方法,則進(jìn)行訪問權(quán)限校驗(yàn),如果通過則返回這個(gè)方法的直接引用,查找過程結(jié)束;如果不通過,則返回 java.lang.IllegalAccessError 異常。
          • 否則,按照繼承關(guān)系對(duì) C 的父類進(jìn)行第 2 步的搜索和校驗(yàn)過程。
          • 如果始終都沒有找到合適的方法,則拋出 java.lang.AbstractMethodError 異常。

          說白了,invokevirtual 會(huì)進(jìn)行查找,查找重載函數(shù),以及權(quán)限驗(yàn)證。找出類型和權(quán)限全符合的函數(shù)進(jìn)行執(zhí)行。

          而 invokestatic 指令用于調(diào)用靜態(tài)方法,即使用 static 關(guān)鍵字修飾的方法;static 修飾的方法是屬于具體類的,因此不需要多余的查找和權(quán)限驗(yàn)證,因此 invokestatic 的性能比 invokevirtual 好!

          時(shí)間倉促,就先聊到這里吧,如有不懂或疑問,歡迎加我微信:codedq,進(jìn)群學(xué)習(xí)!

          瀏覽 65
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  簧片天堂| 园产一级a毛一级a看免费视频 | 婷婷色在线观看 | 免费在线观看岛国人成 | 999在线视频 |