JVM-JMM-并發(fā) 雜記

虛擬機(jī)

編譯

  • 早期編譯(優(yōu)化)

    • 解析與填充符號(hào)表過(guò)程

      • 詞法分析-->Token序列
      • 語(yǔ)法分析-->抽象語(yǔ)法樹(shù)
      • 填充符號(hào)表
    • 注解處理器

    • 語(yǔ)義分析與字節(jié)碼生成

      • 標(biāo)注檢查

        • 變量使用前聲明
        • 變量賦值類(lèi)型匹配
        • 常量折疊
      • 數(shù)據(jù)及控制流分析

        • 局部變量使用前賦值
        • 方法每條路徑具有返回值
        • 所有受查異常被正確處理
      • 解語(yǔ)法糖

      • 字節(jié)碼生成

        將<<init>()和<<clinit>()方法添加進(jìn)語(yǔ)法樹(shù)

  • 運(yùn)行期(優(yōu)化)

    • 解釋器

    • 編譯器

      • Client Compiler(C1)
      • Server Compoler(C2)
    • 分層編譯

      • 第0層,程序解釋執(zhí)行,解釋器不開(kāi)啟性能監(jiān)控功能
      • 第1層,C1編譯,將字節(jié)碼編譯成本地代碼,進(jìn)行簡(jiǎn)單、可靠的優(yōu)化,如有必要將加入性能監(jiān)控邏輯
      • 第2層,C2編譯,將字節(jié)碼編譯成本地代碼,啟用耗時(shí)較長(zhǎng)的優(yōu)化,甚至根據(jù)性能監(jiān)控信息進(jìn)行一些不可靠的激進(jìn)優(yōu)化
    • 編譯觸發(fā):基于計(jì)數(shù)器的熱點(diǎn)探測(cè)

類(lèi)加載

  • 加載

    “全盤(pán)負(fù)責(zé)委托機(jī)制”:

    全盤(pán)負(fù)責(zé),是指當(dāng)一個(gè)ClassLoder裝載一個(gè)類(lèi)時(shí),除非顯示的使用另外一個(gè)ClassLoder,該類(lèi)所依賴(lài)及引用的類(lèi)也由這個(gè)ClassLoder載入;

    委托機(jī)制,是指先委托父類(lèi)裝載器尋找目標(biāo)類(lèi),只有在找不到的情況下才從自己的類(lèi)路徑中查找并裝載目標(biāo)類(lèi)。(可防止用戶(hù)修改JRE基礎(chǔ)類(lèi)庫(kù))

  • 鏈接

    • 驗(yàn)證

    • 準(zhǔn)備

      為類(lèi)變量(static 變量)分配內(nèi)存并設(shè)置初始值(零值)

    • 解析

      將常量池的符號(hào)引用替換為直接引用

  • 初始化

    執(zhí)行<<clinit>方法(static{}塊和類(lèi)變量賦值動(dòng)作)

  • 類(lèi)加載器

    • Bootstrap ClassLoader

      • <JAVA_HOME>\lib
    • Extension ClassLoader

      • <JAVA_HOME>\lib\ext
    • Application ClassLoader

      • 用戶(hù)類(lèi)路徑(ClassPath)
  • Tomcat

    • AppClassLoader load $CATALINA_HOME/bin/bootstrap.jar with CommonClassLoader

    • CommonClassLoader load $CATALINA_HOME/lib

      This class loader contains additional classes that are made visible to both Tomcat internal classes and to all web applications.

    • WebApp1\2... load /WEB-INF/classes;/WEB-INF/lib

      當(dāng)處理從Web應(yīng)用程序的WebappX類(lèi)加載器加載類(lèi)的請(qǐng)求時(shí),該類(lèi)加載器將首先在本地存儲(chǔ)庫(kù)中查找,而不是在查找之前進(jìn)行委托。有例外。作為JRE基類(lèi)的一部分的類(lèi)不能被覆蓋。

      同一個(gè)tomcat中,每個(gè)web app分別有一個(gè)WebAppClassLoader,通過(guò)覆蓋loadclass()方法不做加載委托,實(shí)現(xiàn)各個(gè)web app類(lèi)相互不影響。

    • 熱加載

      同一個(gè)classLoader不能加載相同名字的class,tomcat實(shí)現(xiàn)熱加載,通過(guò)卸載當(dāng)前web context,重新實(shí)例化一個(gè)WebAppClassLoader,重新加載class。

方法調(diào)用

  • 解析調(diào)用

    • 編譯期就確定方法,類(lèi)加載階段,將符號(hào)引用轉(zhuǎn)換成直接引用 static,private,父類(lèi)方法
  • 分派

    • 靜態(tài)分派

      • 編譯階段,根據(jù)傳遞參數(shù)的靜態(tài)類(lèi)型調(diào)用方法,方法重載
      • 多分派,根據(jù)參數(shù)類(lèi)型和方法接收類(lèi)確定
    • 動(dòng)態(tài)分派

      • 運(yùn)行期,根據(jù)傳遞參數(shù)的動(dòng)態(tài)類(lèi)型調(diào)用方法,方法重寫(xiě)
      • 單分派,在編譯階段確定方法簽名,運(yùn)行期只需確定方法實(shí)際接收類(lèi)
  • 解析

    • 發(fā)生在類(lèi)加載解析階段
  • 靜態(tài)分派

    • 發(fā)生在編譯階段,由編譯器根據(jù)參數(shù)靜態(tài)類(lèi)型確定調(diào)用方法

Java內(nèi)存結(jié)構(gòu)

  • 線(xiàn)程私有

    • PC(當(dāng)前線(xiàn)程執(zhí)行字節(jié)碼行號(hào)指示)

    • 虛擬機(jī)棧

      • 棧幀

        • 局部變量表(方法參數(shù)、局部定義變量)

          • 基本數(shù)據(jù)類(lèi)型
          • 引用
        • 操作數(shù)棧

        • 動(dòng)態(tài)連接

        • 方法出口

  • 線(xiàn)程共享

      • 普通對(duì)象

        • 對(duì)象頭

          Java程序的對(duì)象頭固定占8字節(jié)(32位系統(tǒng))或12字節(jié)(64位系統(tǒng)默認(rèn)開(kāi)啟壓縮, 不開(kāi)壓縮為16字節(jié))

          • Mark Word 鎖狀態(tài)、GC年齡等
          • 類(lèi)型指針
          • 數(shù)組長(zhǎng)度(如果是數(shù)組)
        • 實(shí)例數(shù)據(jù)(包括父類(lèi)和子類(lèi)字段)

        • 補(bǔ)齊填充(補(bǔ)齊為8位整數(shù)倍)

    • 方法區(qū)

      Java7之前存在,由于字符串等被放入運(yùn)行時(shí)常量池,導(dǎo)致一系列OOM問(wèn)題,Java8將其挪到本地內(nèi)存,大小可動(dòng)態(tài)增長(zhǎng)。

      • Class對(duì)象-作為訪問(wèn)方法區(qū)入口(HotSpot)

        • 靜態(tài)變量
      • Class字節(jié)碼

        • 魔數(shù)

        • 常量池

          這里是指Class字節(jié)瑪結(jié)構(gòu)中的一部分。

          內(nèi)存中存在運(yùn)行時(shí)常量池,jdk6.0之前存在與方法區(qū)中,jdk7.0存在于堆中,jdk8之后被挪到了本地內(nèi)存中。

          • 符號(hào)/直接引用
        • 類(lèi)或接口的訪問(wèn)標(biāo)志(public、abstract等)

        • 類(lèi)、父類(lèi)、接口索引

        • 字段表集合

        • 方法表集合

          • Code屬性

            • max_locals

              • this+exception params + 本地變量s
            • max_stack

  • 直接內(nèi)存(如NIO的DirectByteBuffer)

GC

  • safepoint:方法返回前、方法call后、循環(huán)末尾跳轉(zhuǎn)、異常跳轉(zhuǎn)

  • OopMap

    • 在safepoint處插入指令,收集GC roots
  • GC Roots

    • 寄存器引用、全局變量、棧幀中本地變量(活著的)
  • 遍歷對(duì)象,調(diào)用finalize

  • GC算法

    • Stop The World

      • 正在運(yùn)行的普通線(xiàn)程

        • Safe Point

          • 位置:方法返回前、方法call后、循環(huán)末尾跳轉(zhuǎn)、異常跳轉(zhuǎn)

          • 實(shí)現(xiàn)

            • OopMap

              • 作用:收集GC Roots,將棧內(nèi)的引用偏移,以及寄存器中存放的引用,記錄到OopMap數(shù)據(jù)結(jié)構(gòu)
              • 位置:每個(gè)類(lèi)有自己的OopMap,在類(lèi)加載時(shí)生成,記錄類(lèi)中引用的偏移;
            • Stop用戶(hù)線(xiàn)程:輪詢(xún)

              • 解釋執(zhí)行

                • JVM設(shè)置一個(gè)2字節(jié)的dispatch tables,輪詢(xún)這個(gè)dispatch tables
              • JIT

                • test指令:JVM將一個(gè)特定的內(nèi)存頁(yè)置為不可讀,通過(guò)test指令將線(xiàn)程掛起
      • 正在運(yùn)行native code的線(xiàn)程

        當(dāng)VM thread看到一個(gè)Java線(xiàn)程在執(zhí)行native code,它不需要等待這個(gè)Java線(xiàn)程進(jìn)入阻塞狀態(tài),因?yàn)楫?dāng)Java線(xiàn)程從執(zhí)行native code返回的時(shí)候,Java線(xiàn)程會(huì)去檢查safepoint看是否要block(When returning from the native code, a Java thread must check the safepoint _state to see if we must block)

        感覺(jué)和safe region頗相似。

      • 阻塞的線(xiàn)程

        • Safe Region

          線(xiàn)程阻塞時(shí),標(biāo)識(shí)自己進(jìn)入safe region,此時(shí)該線(xiàn)程引用關(guān)系不會(huì)變化,JVM可安全的標(biāo)記;

          線(xiàn)程被喚醒時(shí),先檢查是否已經(jīng)完成根節(jié)點(diǎn)枚舉(或是完成整個(gè)GC,視不同垃圾回收器而定),如果完成,則繼續(xù)執(zhí)行,否則掛起知道收到可以安全離開(kāi)safe region的信號(hào)為止。

    • 遍歷對(duì)象

      • GC Roots
      • finalize
      • 標(biāo)記垃圾對(duì)象
    • 清理垃圾對(duì)象

      • 新生代:基于復(fù)制

        • Eden
        • from Survivor
        • to Survivor
      • 老年代:基于標(biāo)記清除、標(biāo)記整理

  • 垃圾收集器

    • 新生代

      觸發(fā)MinorGC(Young GC)

      虛擬機(jī)在進(jìn)行minorGC之前會(huì)判斷老年代最大的可用連續(xù)空間是否大于新生代的所有對(duì)象總空間
      
      
      
      1、如果大于的話(huà),直接執(zhí)行minorGC
      
      
      
      2、如果小于,判斷是否開(kāi)啟HandlerPromotionFailure,沒(méi)有開(kāi)啟直接FullGC
      
      
      
      3、如果開(kāi)啟了HanlerPromotionFailure, JVM會(huì)判斷老年代的最大連續(xù)內(nèi)存空間是否大于歷次晉升的大小,如果小于直接執(zhí)行FullGC
      
      
      
      4、如果大于的話(huà),執(zhí)行minorGC
      
      • Serial

        • 參數(shù)

          • -XX:SurvivorRatio
          • -XX:PretenureSizeThreshold
          • -XX:HandlePromotionFailure
        • 組合:CMS、Serial Old

      • ParNew

        • 參數(shù)

          • -XX:SurvivorRatio
          • -XX:PretenureSizeThreshold
          • -XX:HandlePromotionFailure
          • -XX:ParallelGCThreads
        • 組合:CMS、Serial Old

      • Parellel Scavenge

        • 特點(diǎn):關(guān)注吞吐量 用戶(hù)代碼運(yùn)行時(shí)間/(GC + 用戶(hù)... )

        • 參數(shù)

          • -XX:MaxGCPauseMillis(通過(guò)調(diào)整新生代大小,比如調(diào)小,時(shí)間減少,但GC更頻繁,吞吐量下降)
          • -XX:GCTimeRatio
          • -XX:+UseAdaptiveSizePolicy(開(kāi)啟后,JVM自動(dòng)調(diào)整新生代大小,SurvivorRatio,晉升老年代年齡等參數(shù)實(shí)現(xiàn)最大吞吐量)
        • 組合:Serial Old、Parallel Old

    • 老年代

      觸發(fā)FullGC

      老年代空間不足

       如果創(chuàng)建一個(gè)大對(duì)象,Eden區(qū)域當(dāng)中放不下這個(gè)大對(duì)象,會(huì)直接保存在老年代當(dāng)中,如果老年代空間也不足,就會(huì)觸發(fā)Full GC。為了避免這種情況,最好就是不要?jiǎng)?chuàng)建太大的對(duì)象。
      

      持久代空間不足

      如果有持久代空間的話(huà),系統(tǒng)當(dāng)中需要加載的類(lèi),調(diào)用的方法很多,同時(shí)持久代當(dāng)中沒(méi)有足夠的空間,就出觸發(fā)一次Full GC
      

      YGC出現(xiàn)promotion failure

      promotion failure發(fā)生在Young GC, 如果Survivor區(qū)當(dāng)中存活對(duì)象的年齡達(dá)到了設(shè)定值,會(huì)就將Survivor區(qū)當(dāng)中的對(duì)象拷貝到老年代,如果老年代的空間不足,就會(huì)發(fā)生promotion failure, 接下去就會(huì)發(fā)生Full GC.
      

      統(tǒng)計(jì)YGC發(fā)生時(shí)晉升到老年代的平均總大小大于老年代的空閑空間

        在發(fā)生YGC是會(huì)判斷,是否安全,這里的安全指的是,當(dāng)前老年代空間可以容納YGC晉升的對(duì)象的平均大小,如果不安全,就不會(huì)執(zhí)行YGC,轉(zhuǎn)而執(zhí)行Full GC。
      

      顯示調(diào)用System.gc

      • CMS

        • 特點(diǎn):關(guān)注響應(yīng)時(shí)間

        • 過(guò)程:初始標(biāo)記,并發(fā)標(biāo)記,重新標(biāo)記,并發(fā)清理

        • CPU占用問(wèn)題

          并發(fā)清理階段,線(xiàn)程數(shù)默認(rèn)為 (CPU數(shù)量+3/4),即占用不少于25%的CPU資源,當(dāng)CPU數(shù)量降低時(shí)占用更多。

        • 浮動(dòng)垃圾問(wèn)題:并發(fā)清理階段,預(yù)留空間給用戶(hù)線(xiàn)程使用,產(chǎn)生的垃圾下次GC處理,若空間不足,使用Serial Old重新回收Old區(qū),導(dǎo)致停頓時(shí)間過(guò)長(zhǎng)

          為了在并發(fā)清理階段不STW,需要預(yù)留部分內(nèi)存用于并發(fā)清理階段的用戶(hù)線(xiàn)程使用,JDK1.5默認(rèn)在老年代68%時(shí)啟動(dòng)并發(fā)清理,JDK1.6提升為92%,通過(guò)-XX:CMSInitiatingOccupancyFraction可設(shè)置。當(dāng)預(yù)留的內(nèi)存不足時(shí),會(huì)出現(xiàn)一次“Concurrent Mode Failure”,使用Serial Old會(huì)暫停用戶(hù)線(xiàn)程重新收集old區(qū)。

        • 垃圾碎片問(wèn)題:CMS采用“標(biāo)記-清除”算法,可能存在空間足夠,卻沒(méi)有連續(xù)可用的空間存放對(duì)象,觸發(fā)full gc,可通過(guò)參數(shù)控制full-gc時(shí)整理內(nèi)存碎片

      • Serial Old(MSC)

      • Parallel Old

    • G1

      • 回收周期

        • 年輕代GC

        • 混合GC

          觸發(fā)時(shí)機(jī):

          當(dāng)老年代分區(qū)占用總堆比例超過(guò)閾值(默認(rèn)45%)時(shí),觸發(fā)混合GC。

          初始標(biāo)記:和下一次年輕代GC一起,STW并行標(biāo)記,收集所有的GC Roots。

          并發(fā)標(biāo)記:多線(xiàn)程并發(fā)協(xié)同標(biāo)示存活對(duì)象圖。

          重新標(biāo)記:STW并行重新標(biāo)記上個(gè)階段產(chǎn)生的新垃圾。

          并行回收:垃圾清理。

        - Full GC

          觸發(fā)時(shí)機(jī):對(duì)象分配失敗時(shí)?

          

          使用串行垃圾收集器對(duì)整個(gè)堆全面壓縮。

          

          G1的設(shè)計(jì)目標(biāo)通過(guò)不斷調(diào)優(yōu)而不再需要full GC。

    - 堆空間調(diào)整

      JVM通過(guò)在Xms和Xmx之間動(dòng)態(tài)調(diào)整堆大小及年輕代大小,以滿(mǎn)足用戶(hù)設(shè)置的GC暫停時(shí)間MaxGCPauseMillis和GCTimeRatio(用戶(hù)線(xiàn)程時(shí)間/GC線(xiàn)程時(shí)間)的目標(biāo)。

    - RSet

        - 引用關(guān)系
引用關(guān)系
        - PRT 粒度?
        - 關(guān)系維護(hù)

          維護(hù)時(shí)機(jī):

          對(duì)象引用關(guān)系變化時(shí)(包括引用賦值、GC移動(dòng)對(duì)象等),觸發(fā)寫(xiě)柵欄代碼,維護(hù)Rset。

          

          若發(fā)生一個(gè)跨區(qū)引用關(guān)系變化,G1垃圾收集器會(huì)將相應(yīng)的card加入到“臟卡片隊(duì)列”?!安l(fā)優(yōu)化線(xiàn)程”會(huì)掃描隊(duì)列中的卡片來(lái)更新RSet。當(dāng)“并發(fā)優(yōu)化線(xiàn)程”來(lái)不及處理不過(guò)來(lái)時(shí),會(huì)掛起用戶(hù)線(xiàn)程,讓用戶(hù)線(xiàn)程也加入到更新Rset。

    - 全局卡片表
全局卡片表
      在任意收集周期,掃描Rset與PRT時(shí),會(huì)將掃描到的引用記錄標(biāo)記到全局卡片表,避免重復(fù)掃描。在收集周期的最后將該表清空,顯示為Clear CT。

    - 工作竊取機(jī)制
    - 收集活動(dòng)圖
收集活動(dòng)圖

深入并發(fā)

Java內(nèi)存模型

  • 模型簡(jiǎn)圖


    模型簡(jiǎn)圖
  • 內(nèi)存操作

    • lock&unlock
    • use&load&read
    • assign&store&write
  • happens before

    • 用途:呈現(xiàn)給程序員的并發(fā)視圖,闡述操作之間的內(nèi)存可見(jiàn)性。
    • 與JMM的關(guān)系


      與JMM的關(guān)系
  • 問(wèn)題:重排序

    • 目的:提高執(zhí)行效率

    • 類(lèi)型

      • 編譯器重排

        JMM “happens before” 規(guī)則確保編譯器禁止特定的編譯器重排。

        • 例子:局部變量和操作數(shù)棧的關(guān)系

        • 普通讀寫(xiě)/volatile讀寫(xiě)


          普通讀寫(xiě)/volatile讀寫(xiě)
          • 當(dāng)?shù)诙€(gè)操作是volatile寫(xiě)時(shí),不管第一個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile寫(xiě)之前的操作不會(huì)被編譯器重排序到volatile寫(xiě)之后
          • 當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí),不管第二個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile讀之后的操作不會(huì)被編譯器重排序到volatile讀之前
          • 當(dāng)?shù)谝粋€(gè)操作是volatile寫(xiě),第二個(gè)操作是volatile讀時(shí),不能重排序
        • final域

          • 寫(xiě)

            • JMM禁止編譯器把final域的寫(xiě)重排序到構(gòu)造函數(shù)之外(確保在return之前)
            • none
      • 處理器重排序

        • 類(lèi)型

          • 指令并行重排

            • 指令并行執(zhí)行,改變沒(méi)有數(shù)據(jù)依賴(lài)性的指令執(zhí)行順序
          • 內(nèi)存系統(tǒng)重排

            • 例子:cpu緩存的執(zhí)行結(jié)果,未及時(shí)同步到內(nèi)存,導(dǎo)致順序錯(cuò)亂
        • 內(nèi)存屏障

          • 類(lèi)型

            • LoadLoad

              • 序列:Load1,Loadload,Load2
              • 作用:確保Load1所要讀入的數(shù)據(jù)能夠在被Load2和后續(xù)的Load指令訪問(wèn)前讀入
            • StoreStore

              • 序列:Store1,StoreStore,Store2
              • 確保Store1的數(shù)據(jù)在Store2以及后續(xù)Store指令操作相關(guān)數(shù)據(jù)之前對(duì)其它處理器可見(jiàn)(例如向主存刷新數(shù)據(jù)
            • LoadStore

              • 序列: Load1; LoadStore; Store2
              • 確保Load1的數(shù)據(jù)在Store2和后續(xù)Store指令被刷新之前讀取
            • StoreLoad

              • 序列: Store1; StoreLoad; Load2
              • 確保Store1的數(shù)據(jù)在被Load2和后續(xù)的Load指令讀取之前對(duì)其他處理器可見(jiàn)
            • ”Enter”與”Load”相同,”Exit”與”Store”相同,除非被原子指令的使用和特性覆蓋

          • 使用對(duì)象

            • volatile

                • volatile讀
                • LoadLoad
                • LoadStore
              • 寫(xiě)

                • StoreStore
                • volatile寫(xiě)
                • StoreLoad
            • synchronized

              • 進(jìn)入管程

                • monitor enter 等價(jià)于 volatile讀
                • LoadLoad(EnterLoad)
                • LoadStore(EnterStore)
              • 退出管程

                • LoadStore(LoadExit)
                • StoreStore(StoreExit)
                • monitor exit 等價(jià)于 volatile寫(xiě)
                • StoreLoad(ExitEnter)
            • CAS

              • 同時(shí)具有volatile讀和寫(xiě)的雙內(nèi)存語(yǔ)義
            • final

              • 寫(xiě)

                • 構(gòu)造函數(shù)開(kāi)始
                • 對(duì)象的final域?qū)慭對(duì)象的final域的成員域?qū)懀⊿tore)
                • StoreStore(return之前)
                • 構(gòu)造函數(shù)return
                • 將對(duì)象的引用賦值給引用變量obj(Store)
                • 初次讀引用變量obj(Load)
                • LoadLoad(讀final域之前)
                • 初次讀引用變量obj指向?qū)ο蟮膄inal域\初次讀引用變量obj指向?qū)ο蟮膄inal域的成員域(Load)


  • Java關(guān)鍵字內(nèi)存模型語(yǔ)義

    • volatile

      對(duì)volatile變量的讀寫(xiě)操作,在語(yǔ)義上和synchronized臨界區(qū)一致,即臨界區(qū)內(nèi)的操作具有原子性,臨界區(qū)前的操作對(duì)臨界區(qū)可見(jiàn),臨界區(qū)內(nèi)的操作對(duì)臨界區(qū)后可見(jiàn)。

  volatile操作具有原子性,包括對(duì)于64位的double或long類(lèi)型變量。

  

  可見(jiàn)性:對(duì)一個(gè)volatile變量的讀,總是能看到(任意線(xiàn)程)對(duì)這個(gè)volatile變量最后的寫(xiě)入。

  原子性:對(duì)任意單個(gè)volatile變量的讀/寫(xiě)具有原子性,但類(lèi)似于volatile++這種復(fù)合操作不具有原子性。

  

  volatile引用或數(shù)組,volatile只能保證引用和數(shù)組引用的可見(jiàn)性,對(duì)于引用指向的對(duì)象、對(duì)象的屬性、數(shù)組的元素,均不能保證可見(jiàn)性。

- synchronized

  pre code A...

  sync{

    臨界區(qū) B...

  }

  after code C...

  順序一致性,通過(guò)臨界區(qū)前后插入蔽障,保證三個(gè)區(qū)域代碼順序執(zhí)行,A happens before B,B happens before C...,但ABC區(qū)域內(nèi)部允許重排序。

- final
  • concurrent包\AQS框架

    • 通用模式

      • 1.聲明volatile共享變量
      • 2.使用CAS的原子條件更新來(lái)實(shí)現(xiàn)線(xiàn)程之間的同步
      • 3.配合以volatile的讀/寫(xiě)和CAS所具有的volatile讀/寫(xiě)的內(nèi)存語(yǔ)義來(lái)實(shí)現(xiàn)線(xiàn)程之間的通信
    • 實(shí)現(xiàn)示意圖


      實(shí)現(xiàn)示意圖

java關(guān)鍵字功能、性能

  • Volatile

    • 保證可見(jiàn)性

    • 內(nèi)存屏障,避免JVM優(yōu)化重排序指令帶來(lái)的并法問(wèn)題

      每次對(duì)volatile變量寫(xiě)之后,編譯器會(huì)在之后插入一條匯編指令,將本CPU內(nèi)存刷到共享內(nèi)存,同時(shí)使其他CPU內(nèi)存無(wú)效化;該操作要求對(duì)volatile寫(xiě)之前的指令全部已經(jīng)完成,所以編譯器不能重排到寫(xiě)之后;

    • 不是線(xiàn)程安全的,可與CAS結(jié)合使用,實(shí)現(xiàn)線(xiàn)程安全

  • Synchronized

    • 偏向鎖--基于鎖總是由同一線(xiàn)程獲得,完全消除鎖開(kāi)銷(xiāo)
    • 輕量級(jí)鎖--基于在沒(méi)有多線(xiàn)程競(jìng)爭(zhēng)的情況下,通過(guò)CAS自旋代替互斥
    • 重量級(jí)鎖--基于互斥鎖,沒(méi)搶到鎖即阻塞
  • ReentrantLock

    • 可讓等待線(xiàn)程放棄等待,執(zhí)行其它任務(wù)

      調(diào)用 可中斷獲取 方法,如果獲取失敗,會(huì)進(jìn)入隊(duì)列,自旋獲取鎖并檢查中斷標(biāo)志,當(dāng)中斷標(biāo)志被置位TRUE,拋出 中斷異常,并取消獲取鎖。

    • 提供公平鎖

    • 一個(gè)鎖可綁定多個(gè)條件變量

CPU緩存帶來(lái)的問(wèn)題

  • 簡(jiǎn)單示意圖


    image.png

    CPU Cache分成了三個(gè)級(jí)別: L1, L2, L3. 級(jí)別越小越接近CPU, 所以速度也更快, 同時(shí)也代表著容量越小. L1是最接近CPU的, 它容量最小, 例如32K, 速度最快,每個(gè)核上都有一個(gè)L1 Cache(準(zhǔn)確地說(shuō)每個(gè)核上有兩個(gè)L1 Cache, 一個(gè)存數(shù)據(jù) L1d Cache, 一個(gè)存指令 L1i Cache). L2 Cache 更大一些,例如256K, 速度要慢一些, 一般情況下每個(gè)核上都有一個(gè)獨(dú)立的L2 Cache; L3 Cache是三級(jí)緩存中最大的一級(jí),例如12MB,同時(shí)也是最慢的一級(jí), 在同一個(gè)CPU插槽之間的核共享一個(gè)L3 Cache.

  • 緩存行(cache line)

    緩存存取粒度單位,一般64字節(jié)。
    通過(guò)命令查看:
    cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
    64

    一個(gè)Java long型占8字節(jié), 所以從一條緩存行上你可以獲取到8個(gè)long型變量. 所以如果你訪問(wèn)一個(gè)long型數(shù)組, 當(dāng)有一個(gè)long被加載到cache中, 你將無(wú)消耗地加載了另外7個(gè). 所以你可以非常快地遍歷數(shù)組.

    perf工具抓取L1cache未命中數(shù)據(jù):
    $ perf stat -e L1-dcache-load-misses java L1CacheMiss

  • 緩存一致性協(xié)議(MESI)

    http://blog.csdn.net/opensure/article/details/46669337

    簡(jiǎn)單總結(jié):
    同一個(gè)cache line可以被緩存在多個(gè)核,當(dāng)核A修改該cache line時(shí),會(huì)導(dǎo)致其他核的cache line無(wú)效(注:1),當(dāng)其他核需要修改該cache line時(shí),會(huì)強(qiáng)制核A將修改的cache line刷回內(nèi)存,然后其他核重新讀取,以此保證一個(gè)cache line只能同時(shí)被一個(gè)核修改。

    注:1
    每個(gè)處理器通過(guò)嗅探在總線(xiàn)上傳播的數(shù)據(jù)來(lái)檢查自己緩存的值是不是過(guò)期了,當(dāng)處理器發(fā)現(xiàn)自己緩存行對(duì)應(yīng)的內(nèi)存地址被修改,就會(huì)將當(dāng)前處理器的緩存行設(shè)置成無(wú)效狀態(tài)。

    • 帶來(lái)的問(wèn)題:偽共享

      多線(xiàn)程程序需要并發(fā)操作volatile變量A和B時(shí),如果AB屬于同一個(gè)cache line,由于MESI協(xié)議,導(dǎo)致同一時(shí)刻只能有一個(gè)線(xiàn)程操作A或B中的一個(gè)變量,影響多線(xiàn)程程序的并發(fā)性能。

      解決:可在AB之間填充無(wú)效變量,使AB處于不同的cache line,解決沖突。

      Lock前綴指令會(huì)引起處理器緩存回寫(xiě)到內(nèi)存。Lock前綴指令導(dǎo)致在執(zhí)行指令期間,聲言處理器的 LOCK# 信號(hào)。在多處理器環(huán)境中,LOCK# 信號(hào)確保在聲言該信號(hào)期間,處理器可以獨(dú)占使用任何共享內(nèi)存。(因?yàn)樗鼤?huì)鎖住總線(xiàn),導(dǎo)致其他CPU不能訪問(wèn)總線(xiàn),不能訪問(wèn)總線(xiàn)就意味著不能訪問(wèn)系統(tǒng)內(nèi)存),但是在最近的處理器里,LOCK#信號(hào)一般不鎖總線(xiàn),而是鎖緩存,畢竟鎖總線(xiàn)開(kāi)銷(xiāo)比較大。在8.1.4章節(jié)有詳細(xì)說(shuō)明鎖定操作對(duì)處理器緩存的影響,對(duì)于Intel486和Pentium處理器,在鎖操作時(shí),總是在總線(xiàn)上聲言LOCK#信號(hào)。但在P6和最近的處理器中,如果訪問(wèn)的內(nèi)存區(qū)域已經(jīng)緩存在處理器內(nèi)部,則不會(huì)聲言LOCK#信號(hào)。相反地,它會(huì)鎖定這塊內(nèi)存區(qū)域的緩存并回寫(xiě)到內(nèi)存,并使用緩存一致性機(jī)制來(lái)確保修改的原子性,此操作被稱(chēng)為“緩存鎖定”,緩存一致性機(jī)制會(huì)阻止同時(shí)修改被兩個(gè)以上處理器緩存的內(nèi)存區(qū)域數(shù)據(jù)。

      一個(gè)處理器的緩存回寫(xiě)到內(nèi)存會(huì)導(dǎo)致其他處理器的緩存無(wú)效。IA-32處理器和Intel 64處理器使用MESI(修改,獨(dú)占,共享,無(wú)效)控制協(xié)議去維護(hù)內(nèi)部緩存和其他處理器緩存的一致性。在多核處理器系統(tǒng)中進(jìn)行操作的時(shí)候,IA-32 和Intel 64處理器能嗅探其他處理器訪問(wèn)系統(tǒng)內(nèi)存和它們的內(nèi)部緩存。它們使用嗅探技術(shù)保證它的內(nèi)部緩存,系統(tǒng)內(nèi)存和其他處理器的緩存的數(shù)據(jù)在總線(xiàn)上保持一致。例如在Pentium和P6 family處理器中,如果通過(guò)嗅探一個(gè)處理器來(lái)檢測(cè)其他處理器打算寫(xiě)內(nèi)存地址,而這個(gè)地址當(dāng)前處理共享狀態(tài),那么正在嗅探的處理器將無(wú)效它的緩存行,在下次訪問(wèn)相同內(nèi)存地址時(shí),強(qiáng)制執(zhí)行緩存行填充。

鎖的一種實(shí)現(xiàn)

  • Synchronized

    • 可重入
  • Lock

public class Counter{

public class Lock{

private boolean isLocked = false;



public synchronized void lock()

    throws InterruptedException{

    while(isLocked){

        wait();

    }

    isLocked = true;

}



public synchronized void unlock(){

    isLocked = false;

    notify();

}

}

  • ReentrantLock

    • 非公平

      public class Lock{

      boolean isLocked = false;

      Thread lockedBy = null;

      int lockedCount = 0;

    public synchronized void lock()

        throws InterruptedException{

        Thread callingThread =

            Thread.currentThread();

        while(isLocked && lockedBy != callingThread){

            wait();

        }

        isLocked = true;

        lockedCount++;

        lockedBy = callingThread;

    }

  

    public synchronized void unlock(){

        if(Thread.curentThread() ==

            this.lockedBy){

            lockedCount--;

  

            if(lockedCount == 0){

                isLocked = false;

                notify();

            }

        }

    }

  

    ...

  }

- 公平

  public class FairLock {

      private boolean           isLocked       = false;

      private Thread            lockingThread  = null;

      private List<QueueObject> waitingThreads =

              new ArrayList<QueueObject>();

  

    public void lock() throws InterruptedException{

      QueueObject queueObject           = new QueueObject();

      boolean     isLockedForThisThread = true;

      synchronized(this){

          waitingThreads.add(queueObject);

      }

  

      while(isLockedForThisThread){

        synchronized(this){

          isLockedForThisThread =

              isLocked || waitingThreads.get(0) != queueObject;

          if(!isLockedForThisThread){

            isLocked = true;

             waitingThreads.remove(queueObject);//加到鎖的線(xiàn)程移除喚醒對(duì)象

             lockingThread = Thread.currentThread();

             return;

           }

        }

        try{

          queueObject.doWait();

        }catch(InterruptedException e){

          synchronized(this) { waitingThreads.remove(queueObject); }

          throw e;

        }

      }

    }

  

    public synchronized void unlock(){

      if(this.lockingThread != Thread.currentThread()){

        throw new IllegalMonitorStateException(

          "Calling thread has not locked this lock");

      }

      isLocked      = false;

      lockingThread = null;

      if(waitingThreads.size() > 0){

        waitingThreads.get(0).doNotify();

      }

    }

  }

  

  public class QueueObject {

  

      private boolean isNotified = false;

  

      public synchronized void doWait() throws InterruptedException {

  

      while(!isNotified){

          this.wait();

      }

  

      this.isNotified = false;

  

  }

  

  public synchronized void doNotify() {

      this.isNotified = true;

      this.notify();

  }

  

  public boolean equals(Object o) {

      return this == o;

  }

  

  }
  • ReadWriteLock

    public class ReadWriteLock{

    private int readers = 0;

    private int writers = 0;

    private int writeRequests = 0;

public synchronized void lockRead() 

    throws InterruptedException{

    while(writers > 0 || writeRequests > 0){

        wait();

    }

    readers++;

}



public synchronized void unlockRead(){

    readers--;

    notifyAll();

}



public synchronized void lockWrite() 

    throws InterruptedException{

    writeRequests++;



    while(readers > 0 || writers > 0){

        wait();

    }

    writeRequests--;

    writers++;

}



public synchronized void unlockWrite() 

    throws InterruptedException{

    writers--;

    notifyAll();

}

}

并發(fā)集合、線(xiàn)程池

  • 線(xiàn)程池-ThreadPoolExecutor

    • 配置

      • 數(shù)量配置

        • 根據(jù)任務(wù)性質(zhì)

          • cpu密集型--N+1
          • IO密集型--2N
          • 混合型--如果cpu消耗和IO相當(dāng),拆分為cpu密集型和IO密集型
      • 任務(wù)隊(duì)列配置

        • 任務(wù)有優(yōu)先級(jí)--采用PriorityBlockingQueue
  • 并發(fā)集合

    • ConcurrentModificationException

      Iterator內(nèi)部維護(hù)一個(gè)expectedModCount,當(dāng)直接通過(guò)集合進(jìn)行修改時(shí),集合的modCount會(huì)改變,但expectedModCount不會(huì)改變,當(dāng)Iterator調(diào)用hasNext()和Next()時(shí),會(huì)比較modCount==expectedModCount,如果不等,說(shuō)明集合被外部修改,拋出異常

    • ConcurrentHashMap

      • 分段鎖

        size,未避免成為熱點(diǎn)域,每個(gè)鎖維護(hù)鎖范圍內(nèi)的bucket的計(jì)數(shù),size()函數(shù)返回所有計(jì)數(shù)之和,因?yàn)閟ize()函數(shù)不會(huì)同時(shí)加上所有的鎖,返回的值不精確,isEmpty()同理。

      • 弱一致迭代器

        當(dāng)?shù)呀?jīng)開(kāi)始,集合元素被修改,若被刪除,迭代到該元素時(shí),next()不會(huì)返回該元素;若增加了元素,next()可能返回該元素,也可能不。

    • 隊(duì)列的同步問(wèn)題

    • 阻塞隊(duì)列

      • ArrayBlockingQueue
      • LinkedBlockingQueue
      • PriorityBlockingQueue
      • DelayQueue
      • SynchronousQueue
      • LinkedTransferQueue
      • LinkedBlockingDeque

零散知識(shí)點(diǎn)

常用命令

  • jps

    顯示當(dāng)前所有java進(jìn)程pid

  • jmap

    主要用于打印指定Java進(jìn)程(或核心文件、遠(yuǎn)程調(diào)試服務(wù)器)的共享對(duì)象內(nèi)存映射或堆內(nèi)存細(xì)節(jié)

  • jstack

    查看線(xiàn)程信息

  • jstat

    主要利用JVM內(nèi)建的指令對(duì)Java應(yīng)用程序的資源和性能進(jìn)行實(shí)時(shí)的命令行的監(jiān)控,包括了對(duì)Heap size和垃圾回收狀況的監(jiān)控

  • jhat

  • jinfo?

  • javap?

    反編譯

String

  • intern()

    jdk6.0:
    常量池存在于方法區(qū),和堆完全隔離,常量池只能存放字符串,intern()方法將字符串放入常量池,并返回指向方法區(qū)常量池相應(yīng)字符串的引用;若字符串已存在,返回已存在字符串的引用。
    jdk7.0:
    常量池存在于堆,常量池可存放指向堆內(nèi)String Obj的引用,string_obj.intern()方法將指向string_obj的引用存入常量池,并返回指向string_obj的引用;若字符串已存在,返回已存在字符串的引用;檢查常量池字符串是否存在時(shí),池內(nèi)引用指向的string_obj的值也要算作存在。

    String s = new String("s");//創(chuàng)建兩個(gè)對(duì)象,一個(gè)堆內(nèi)string_obj,一個(gè)常量池內(nèi)字符串;
    String s = new String("s1") + new String("s2");//創(chuàng)建五個(gè)對(duì)象,堆內(nèi)string_obj_s("s1s2"),string_obj("s1"),string_obj("s2"),常量池字符串"s1","s2".
    String s = "test";//該字符串直接插入常量池;

  • substring()

    • JDK6

      • String Obj

        • char value[]
        • int offset
        • int count
      • 實(shí)現(xiàn):以char[]為入?yún)ew String(),String Obj引用傳入的char [];substring產(chǎn)生一個(gè)新的String Obj,引用源String的value,通過(guò)改變offset和count實(shí)現(xiàn)。

      • 缺點(diǎn):子串一直拿著父串的引用,導(dǎo)致父不能釋放內(nèi)存

      • 解決:通過(guò)String sub = s.substring() + "";返回一個(gè)包含新value的串

    • JDK7

      • String Obj

        • char value[]
      • 解決JDK6問(wèn)題:substring()直接返回一個(gè)新的數(shù)組

  • equals() & hashcode()

反射&代理&注解

  • 反射

    通過(guò)反射API,在運(yùn)行狀態(tài)獲取類(lèi)的所有屬性和方法,構(gòu)造對(duì)象,并訪問(wèn)屬性和方法。

    public static Class<?> forName(String className)
    throws ClassNotFoundException
    public Method[] getMethods()
    throws SecurityException
    public Object invoke(Object obj,
    Object... args)
    throws IllegalAccessException,
    IllegalArgumentException,
    InvocationTargetException

  • 動(dòng)態(tài)代理

    • 例子

      功能:禁止使用List接口中的add方法。

      public List getList(final List list) {
      return (List) Proxy.newProxyInstance(DummyProxy.class.getClassLoader(), new Class[] { List.class },
      new InvocationHandler() {
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("add".equals(method.getName())) {
      throw new UnsupportedOperationException();
      }
      else {
      return method.invoke(list, args);
      }
      }
      });
      }

- 簡(jiǎn)單原理

  JVM創(chuàng)建一個(gè)代理類(lèi):
  public final class $Proxy0 extends Proxy implements List
  代理類(lèi)保存InvocationHandler對(duì)象:
  public $Proxy(InvocationHandler invocationhandler)
  {
    super(invocationhandler);
  }
  代理類(lèi)實(shí)現(xiàn)接口:
  public final List_Method()
  {
    super.h.invoke(this, m3, null); 
    // 實(shí)際上就是調(diào)用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法
  }
  • 注解

    • 注解定義

      @Target(ElementType.METHOD) //可注解范圍,方法
      @Retention(RetentionPolicy.RUNTIME) //注解有效范圍,運(yùn)行時(shí)有效
      public @interface MyAnnotation
      {
      String myKey() default "hello"; //定義一個(gè)注解屬性,類(lèi)型為String,默認(rèn)值為“hello”
      }

    • 注解實(shí)施

      @MyAnnotation(myKey="nihao")
      public void method()
      {
      ...
      }

    • 注解處理器

      注解本身不會(huì)對(duì)代碼造成影響,需要注解處理器解析注解,實(shí)現(xiàn)注解功能。

      使用反射API:
      使用了注解的Class->Class中使用了注解的方法和屬性->方法和屬性的注解->注解值->實(shí)現(xiàn)自定義注解功能;

      public class myAnnotationHandler
      {
      public static void processAnnotations(Object obj)
      {
      Class<?> class = obj.getClass();
      for (Method m : class.getDeclaredMethods() )
      {
      MyAnnotation myAnnotation = m.getAnnotation(MyAnnotation.class);
      if (myAnnotation != null)
      {
      String annotationKey = myAnnotation.myKey();
      ...
      //實(shí)現(xiàn)自定義注解功能
      }
      }
      }
      }

序列化

1、在Java中,只要一個(gè)類(lèi)實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。

2、通過(guò)ObjectOutputStream和ObjectInputStream對(duì)對(duì)象進(jìn)行序列化及反序列化

3、虛擬機(jī)是否允許反序列化,不僅取決于類(lèi)路徑和功能代碼是否一致,一個(gè)非常重要的一點(diǎn)是兩個(gè)類(lèi)的序列化 ID 是否一致(就是 private static final long serialVersionUID)

4、序列化并不保存靜態(tài)變量。

5、要想將父類(lèi)對(duì)象也序列化,就需要讓父類(lèi)也實(shí)現(xiàn)Serializable 接口。

6、Transient 關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設(shè)為初始值,如 int 型的是 0,對(duì)象型的是 null。

7、服務(wù)器端給客戶(hù)端發(fā)送序列化對(duì)象數(shù)據(jù),對(duì)象中有一些數(shù)據(jù)是敏感的,比如密碼字符串等,希望對(duì)該密碼字段在序列化時(shí),進(jìn)行加密,而客戶(hù)端如果擁有解密的密鑰,只有在客戶(hù)端進(jìn)行反序列化時(shí),才可以對(duì)密碼進(jìn)行讀取,這樣可以一定程度保證序列化對(duì)象的數(shù)據(jù)安全。

  • 用途:對(duì)象持久化(下盤(pán)及網(wǎng)絡(luò)傳輸)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容