<深入理解JAVA虛擬機(jī)>讀書筆記

概要

  • 基礎(chǔ)知識
  • 內(nèi)存管理
  • 執(zhí)行引擎
  • 編譯與代碼優(yōu)化
  • 高效并發(fā)

64位虛擬機(jī) VS 32位虛擬機(jī)

  • JVM虛擬機(jī) 性能 64位 < 32位

    • 64位內(nèi)存占用更多
    • 64位性能弱于32位虛擬機(jī)器:垃圾回收管理更大內(nèi)存,JIT的效率也有不同
  • 普通對象指針壓縮,可以在64位虛擬機(jī)上優(yōu)化性能

-XX:+ UseCompressedOops

ps:以上是書本結(jié)論,但是物理機(jī)上的表現(xiàn)看,64位性能更好,一方面是指令更短,ALU(算術(shù)邏輯運(yùn)算器)和寄存器可以處理更大的整數(shù),特別是在高精度計(jì)算上64位更為卓越,32位寄存器有4G內(nèi)存空間的限制,64位內(nèi)存空間可以擴(kuò)展得更大。長期來看64位處理器和64位虛擬機(jī)會是趨勢,未來應(yīng)該還是首選64位物理機(jī),虛擬機(jī)也會是64位虛擬機(jī)。

編譯JDK

調(diào)試工具GDB

  • todo:...

Java內(nèi)存區(qū)域 & 溢出異常

jvm結(jié)構(gòu)

jvm結(jié)構(gòu)
  • 【程序計(jì)數(shù)器】

    • 線程私有,不會OOM,Native方法為Undefined,當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。
  • 【JAVA虛擬機(jī)?!?/p>

    • 每一個方法的執(zhí)行,就是一個棧幀的入棧到出棧
    • 棧幀: 局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口
    • 局部變量表:基本數(shù)據(jù)類型,對象引用,和returnAddress
    • 長度64的long和double需要占用2個局部變量的空間(slot)其余的占用1個
    • 局部變量表需要的空間在編譯器件確定,運(yùn)行期間不會改變
  • 【本地方法棧】

    • Native方法
  • 【Java堆】

    • 實(shí)例和數(shù)組在堆上分配
    • JIT編譯器的發(fā)展和逃逸技術(shù)成熟【棧上分配 + 標(biāo)量替換】實(shí)例不一定在堆上創(chuàng)建。
    • GC主要是針對的堆
    • 收集器主要是:分代收集
    • 新生代,老年代,...
  • 【方法區(qū)】

    • 線程共享,虛擬機(jī)加載的類信息,常量,靜態(tài)變量,即時編譯器編譯后的代碼數(shù)據(jù)
    • 永久代
    • 【運(yùn)行時常量池】是方法區(qū)的一部分,運(yùn)行時常量池和class文件常量池的區(qū)別是具備動態(tài)性。

其他:

  • 【直接內(nèi)存】
* 不屬于運(yùn)行時數(shù)據(jù)區(qū)
* Nio中引入了channel和buffer的io方式,使用native函數(shù)庫直接分配堆外內(nèi)存
* 然后通過存儲在java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用操作
* 避免了java堆和native堆中來回復(fù)制數(shù)據(jù),提高了性能。

對象的創(chuàng)建過程N(yùn)ew

1 檢查在常量池中是否有一個類的符號引用,如果有,且已經(jīng)被加載,解析,初始化過,就返回,否則重新創(chuàng)建
2 分配內(nèi)存,指針碰撞(規(guī)整的空間移動一段距離),空閑列表(適用于不規(guī)整的空間)

  • 注意分配空間如何線程安全:
  • CAS 失敗重試
  • 給每個線程預(yù)分配一段空間(TLAB),避免出現(xiàn)線程共享區(qū)域

3 必要的類設(shè)置:實(shí)例歸屬,元數(shù)據(jù)信息,hash碼,GC分代年紀(jì)等等
4 執(zhí)行init方法

對象的內(nèi)存布局

對象在內(nèi)存中布局分為3個區(qū)域:對象頭,實(shí)例數(shù)據(jù),對齊填充

  • 對象頭:運(yùn)行時數(shù)據(jù) 和 類型指針
    類型指針,指向它類元數(shù)據(jù)的指針,注意查找對象的數(shù)據(jù)不一定要經(jīng)過對象本身(之后會說)
    ps:如果對象是一個java數(shù)組,還需要記錄數(shù)組長度
  • 實(shí)例數(shù)據(jù),盡量是相同大小的分配在一起
  • 對齊填充,保證數(shù)據(jù)是8的整數(shù)倍

對象的訪問定位

句柄 和 直接指針

  • 句柄


    image.png
  • 直接指針


    image.png
  • 直接指針只有2次指針訪問,速度快
  • 句柄的好處是,reference穩(wěn)定,對象改變影響小

垃圾回收

標(biāo)記是否垃圾的算法

  • 引用計(jì)數(shù)
    • 缺陷:互相引用的時候不會被回收
  • 可達(dá)性分析,GCroot向下搜索
    • 可作為GCroot的對象
      • 虛擬機(jī)棧的棧幀 中的本地變量表 的引用的 對象
      • 方法區(qū)中的類靜態(tài)屬性引用的對象
      • 方法區(qū)中的常量引用的對象
      • 本地方法棧中JNI(即Native方法)引用的對象

引用

  • 強(qiáng)引用:引用還在,垃圾回收不會回收
  • 軟引用:內(nèi)存溢出前回收
  • 弱引用:每次垃圾回收都回收
  • 虛引用 Phantom Reference:被垃圾回收的時候可以收到系統(tǒng)通知

回收

  • 1 可達(dá)性分析之后先標(biāo)記可以回收
  • 2 如果沒有覆蓋finalize()方法,或者finalize()被調(diào)用過,則沒有必要執(zhí)行
  • 3 如果有必要執(zhí)行,進(jìn)入F-Queue隊(duì)列中,之后被JVM創(chuàng)建的一個線程回收

注意,finalize()運(yùn)行代價(jià)高,不保證調(diào)用順序,所以不適合用來關(guān)閉外部資源,應(yīng)該用try-finally來關(guān)閉資源。

回收方法區(qū)(永久代)

  • 廢棄常量
  • 無用的類
    • 類的所有實(shí)例被回收
    • 加載類的ClassLoader被回收
    • 類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,也無法通過反射訪問類的方法

GC的算法

  • 標(biāo)記清除
  • 復(fù)制:回收新生代
  • 標(biāo)記整理
  • 分代收集

GC的stop the world:

  • 大部分GC在做垃圾回收都會掛起所有用戶線程,保證回收的數(shù)據(jù)一致性(比如 Copying這種算法)
  • 枚舉根結(jié)點(diǎn),一定會stop the world
  • 使用安全點(diǎn),gc的時候讓線程運(yùn)行到安全點(diǎn),只有到達(dá)了安全點(diǎn)才能進(jìn)行GC
  • 如果線程沒有cpu時間,也無法走到安全點(diǎn),那么就要用安全區(qū)域來解決,一段代碼中,引用關(guān)系不變化,這個區(qū)域的任意地方GC都是安全的。

性能要求高的環(huán)境難以接受,所以要減少頻繁的GC,CMS分為幾個階段:
*【initial Mark】

  • Concurrent Mark
  • 【ReMark】
  • Concurrent sweep

initial Mark和Remark會Stop the world. Concurrent Mark和Concurrent Sweep會和用戶線程一起運(yùn)行。雖然CMS減少了stop the world的次數(shù),不可避免地讓整體GC的時間拉長了


image.png

不同的垃圾收集器,沒有一個通用的,HotSpot實(shí)現(xiàn)了很多:
新生代的:大多是復(fù)制算法

  • Serial:單線程收集器,有停頓,簡單,適用于允許停頓的客戶端程序。
  • ParNew:Serial的多線程版本,性能優(yōu),除了Serial之外,只有他能和CMS配合工作。在JDK5之后,老年代選用CMS,新生代就只能用Serial和ParNew了。單核環(huán)境中,ParNew的性能不如Serial(線程交錯的開銷)
  • 【Parallel Scavenge】,關(guān)注的不是減少停頓時間,而是達(dá)到一個吞吐量(代碼運(yùn)行時間/(代碼運(yùn)行時間 + GC時間)),所以指定的參數(shù)也是垃圾收集的最大停頓時間,以及吞吐量大小

老年代的:大多是標(biāo)記清除 or 標(biāo)記整理算法

  • Serial Old
  • Praallel Old
  • 【CMS】,并發(fā),標(biāo)記清除

CMS:

  • 幾個階段:初始標(biāo)記,并發(fā)標(biāo)記,重新標(biāo)記,并發(fā)回收
  • 優(yōu)點(diǎn):性能,停頓少
  • 缺點(diǎn):標(biāo)記清除產(chǎn)生碎片多,無法處理浮動垃圾,并發(fā)清理階段可能還產(chǎn)生新的垃圾,會出現(xiàn)Concurrent Mode Failure,導(dǎo)致額外產(chǎn)生一次GC

G1收集器 VS CMS

  • G1更代表未來
  • G1過程:初始標(biāo)記,并發(fā)標(biāo)記,最終標(biāo)記,篩選回收
  • G1分代收集,老年代和年輕代都可以用
  • G1 標(biāo)記-整理,碎片少
  • G1 可預(yù)測的停頓,垃圾收集時間不超過xx毫秒有可控參數(shù)

對象分配策略

上圖還需要補(bǔ)充一個點(diǎn)“空間分配擔(dān)?!?/p>

第四章 故障 & 性能監(jiān)控工具

工具列表

  • jps:查看進(jìn)程,主類
-q 本地虛擬機(jī)唯一id
-l 主類全名
-v 啟動的JVM參數(shù)
-m 給主類的參數(shù)
  • jstat:虛擬機(jī)統(tǒng)計(jì)信息監(jiān)控
- class 類裝載,總空間等
- gc 堆的狀況,各個區(qū)域
- compiler jit編譯過的方法和耗時
  • jhat:可視化
  • jinfo:查看和調(diào)整JVM配置信息
  • jstack:查看棧

  • jmap: 查看堆
- dump 堆快照
- heap 堆詳情
- histo 堆統(tǒng)計(jì)

調(diào)優(yōu)經(jīng)驗(yàn)

  • 深夜觸發(fā)定時任務(wù)做 fullGC,保持內(nèi)存在一個穩(wěn)定水平
  • JDK 64 在堆內(nèi)存溢出的時候 dump 會有十幾個G的文件,大文件的dump和分析都比較困難。

類文件的結(jié)構(gòu)

傳統(tǒng)編譯器: 類文件 --> 編譯 --> 本地機(jī)器碼
java: 類文件(.java)--> 編譯 --> 字節(jié)碼 (.class)--> jvm

構(gòu)成

    • 類文件構(gòu)成
  • magic
  • magic_version
  • major_version
  • constant_pool_count
  • constant_pool
  • access_flags
  • this_class
  • super_class
  • interfaces_count
  • interfaces
  • fields_count
  • fields
  • methods_count
  • methods
  • attributes_count
  • attributes

指令集

    • 指令集

類加載機(jī)制

    • 類加載過程

準(zhǔn)備階段

1 如果是類變量,會在解析階段賦初始化的0值。
比如 static int value=123,這個階段value是0
分配在【方法區(qū)】
而value= 123的putstatic是在編譯后,初始化階段,放在類構(gòu)造器<clinit>()方法中
如果是實(shí)例變量,會在對象實(shí)例化的時候,隨著對象一起分配在java堆中

對于常量ConstantValue中有值
static final int value = 123
會在編譯的時候?yàn)関alue生成ConstantValue屬性,在準(zhǔn)備階段value就會變?yōu)?23

  • 類的初始化過程與類的實(shí)例化過程的異同?
類的初始化是指:
類加載過程中的初始化階段對類變量按照程序猿的意圖進(jìn)行賦值的過程

類的實(shí)例化是指:
在類完全加載到內(nèi)存中后創(chuàng)建對象的過程。

類實(shí)例化的一般過程是:
父類的類構(gòu)造器<clinit>() 
-> 子類的類構(gòu)造器<clinit>() 
-> 父類的成員變量和實(shí)例代碼塊
-> 父類的構(gòu)造函數(shù) 
-> 子類的成員變量和實(shí)例代碼塊 
-> 子類的構(gòu)造函數(shù)。

編譯優(yōu)化

編譯器,解釋器,通常jvm采用的是混合模式,但是可以通過參數(shù)來指定編譯或者解釋。

對于熱點(diǎn)代碼(采樣探測 or 計(jì)數(shù)器),會被即時編譯器編譯,
即時編譯的優(yōu)化有若干,這里忽略

其他優(yōu)化策略:

  • 公共子表達(dá)式消除
  • 數(shù)組邊界檢查消除
  • 方法內(nèi)聯(lián)(減少引用)
  • 逃逸分析:對象的棧上分配,同步消除,標(biāo)量替換
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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