<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>

          一行代碼引發(fā)的性能暴跌 10 倍!

          共 4332字,需瀏覽 9分鐘

           ·

          2023-09-18 20:10

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

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

          編輯:業(yè)余草

          來源:juejin.cn/post/7276999034962280508

          推薦:https://t.zsxq.com/12FEd8lJL

          自律才能自由

          今天給大家分享一個(gè),最近遇到的性能優(yōu)化問題。線上的業(yè)務(wù)邏輯比較復(fù)雜,本文只提供相關(guān)代碼的簡化程序。

          下面我們直接上測試代碼。

          代碼測試

          import com.google.common.base.Stopwatch;
          import java.util.concurrent.TimeUnit;
          public class StackTest {
              public static void main(String[] args) {
                  Stopwatch started = new Stopwatch();
                  started.start();
                  User user = null;
                  for (long i = 0; i < 1000_000_000; i++) {
                      user = new User();
                  }
                  started.stop();
                  System.out.println(started.elapsed(TimeUnit.MILLISECONDS) + "ms");
                  //不加打印 300ms
                  //加了打印 3000ms
          //        System.out.println(user);
              }
          }

          class User {
              private int age;
              private String userName;

              public int getAge() {
                  return age;
              }

              public void setAge(int age) {
                  this.age = age;
              }

              public String getUserName() {
                  return userName;
              }

              public void setUserName(String userName) {
                  this.userName = userName;
              }
          }

          上面的一個(gè)簡單的代碼是測試 Java 創(chuàng)建對象的性能,如果沒有 System.out.println(user); 輸出的時(shí)間是 300ms左右,如果加上性能是 3000ms 左右,整整慢了 10 倍左右。(具體需要時(shí)間根據(jù)電腦的配置決定)。

          有人可能會(huì)說,System.out.println函數(shù)有鎖(都 9102 了,你還不知道 System.out.println 的危害!),但要注意,這里是單線程,Java 會(huì)鎖消除

          看似很簡單的代碼,卻會(huì)帶來這樣的性能消耗,確實(shí)很讓人費(fèi)解。為了弄清楚這個(gè)問題,我們需要討論下,java 代碼分配的規(guī)則。

          對象分配規(guī)則

          在前面的博客已經(jīng)提過 Java 對象的分配過程,具體流程圖如下:

          棧上分配

          棧上分配是 Java 虛擬機(jī)提供的一項(xiàng)優(yōu)化技術(shù),將線程私有的對象打散分配在棧上,棧上分配的對象回收直接 POP 出站,不需要垃圾回收器的介入,效率很高。當(dāng)然棧上分配也需要一些特殊的條件:

          1. 棧空間小,對于大對象無法實(shí)現(xiàn)棧上分配
          2. 對象不能出現(xiàn)逃逸(JVM 參數(shù):-XX:+DoEscapeAnalysis
          3. 對象可以進(jìn)行標(biāo)量替換,即是使用字段來表示對象(-XX:+EliminateAllocations)。

          如 demo 所示,我們可以是用 age 和 username 兩個(gè)字段來代替 User 對象。

          TLAB 分配

          TLAB Thread Local Allocation Buffer, 即:線程本地分配緩存。這是一塊線程專用的內(nèi)存分配區(qū)域。TLAB 占用的是 eden 區(qū)的空間。在TLAB 啟用的情況下(默認(rèn)開啟),JVM會(huì)為每一個(gè)線程分配一塊TLAB區(qū)域。

          使用 TLAB 是為了加速對象的分配。由于對象一般分配在堆上,而堆是線程共用的,因此可能會(huì)有多個(gè)線程在堆上申請空間,而每一次的對象分配都必須線程同步,會(huì)使分配的效率下降。

          考慮到對象分配幾乎是 Java中 最常用的操作,因此 JVM 使用了 TLAB 這樣的線程專有區(qū)域來避免多線程沖突,提高對象分配的效率。

          同樣,TLAB 空間一般不會(huì)太大(占用 eden 區(qū)),所以大對象無法進(jìn)行 TLAB 分配,只能直接分配到堆上。

          分配策略

          一個(gè)100KB的TLAB區(qū)域,如果已經(jīng)使用了80KB,當(dāng)需要分配一個(gè)30KB的對象時(shí),TLAB是如何分配的呢?可以有兩種情況:

          1. 廢棄當(dāng)前的 TLAB,重新申請;
          2. 將這個(gè) 30KB 的對象直接分配到堆上,保留當(dāng)前 TLAB(當(dāng)有小于 20KB 的對象請求 TLAB 分配時(shí)可以直接使用該 TLAB 區(qū)域)。

          JVM選擇的策略是:在虛擬機(jī)內(nèi)部維護(hù)一個(gè)叫 refill_waste 的值,當(dāng)請求對象大于 refill_waste 時(shí),會(huì)選擇在堆中分配,反之,則會(huì)廢棄當(dāng)前 TLAB,新建 TLAB來分配新對象。【默認(rèn)情況下,TLAB和refill_waste都是會(huì)在運(yùn)行時(shí)不斷調(diào)整的,使系統(tǒng)的運(yùn)行狀態(tài)達(dá)到最優(yōu)。】

          JVM參數(shù)解析

          參數(shù) 作用 備注
          -XX:+UseTLAB 啟用TLAB 默認(rèn)啟用
          -XX:TLABRefillWasteFraction 設(shè)置允許空間浪費(fèi)的比例 默認(rèn)值:64,即:使用1/64的TLAB空間大小作為refill_waste值
          -XX:-ResizeTLAB 禁止系統(tǒng)自動(dòng)調(diào)整TLAB大小
          -XX:TLABSize 指定TLAB大小 單位:B

          Demo 分析

          通過上面的分析,可以剖析出原因了,在使用打印的時(shí)候?qū)е铝?user 對象的逃逸,所以導(dǎo)致在棧上分配條件不滿足,只能在堆上分配,這樣就會(huì)導(dǎo)致頻繁的 GC,效率低下。

          如果我們再使用(-XX:+UseTLAB)關(guān)閉 TLAB分配原則,則會(huì)導(dǎo)致分配的速度又會(huì)降低一點(diǎn)(TLAB 一般會(huì)對多線程競爭分配的時(shí)候提升比較明顯,此處不再驗(yàn)證)

          瀏覽 1064
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  久久久久久久久免费观看完整版 | 黄级网站特一免费观看 | 欧美性爱-91Q | 国产一级电影在线看 | A片在线免费观看视频 |