JVM相關(guān)總結(jié)

[JVM]

雖然大部分情況下不需要我們直接對Java內(nèi)存方面進(jìn)行直接操作,但是了解其中的原理和調(diào)優(yōu)還是比較重要的,JVM中的總結(jié)如下:

[運(yùn)行數(shù)據(jù)區(qū)]

  1. 程序計數(shù)器
    每個線程都有一個獨(dú)立的程序計數(shù)器,是線程私有的。
    若是正在運(yùn)行一個方法,計數(shù)器記錄正在執(zhí)行虛擬機(jī)字節(jié)碼指令地址;
    執(zhí)行的是native的方法,計數(shù)器為空(Undefined),不存在OutOfMemoryError

  2. 虛擬機(jī)棧
    線程私有,java方法執(zhí)行的內(nèi)存模型。
    每個方法運(yùn)行的時候會創(chuàng)建一個棧幀,
    棧幀存儲著 ->
    (a)局部變量
    用于存放方法參數(shù)和方法內(nèi)部定義的局部變量
    包括基本數(shù)據(jù)類型boolean,byte,char,int,short,double,long.float,對象引用類型reference->不等于對象本身,returnAddress
    (b)操作數(shù)棧
    后入先出
    (c)動態(tài)鏈接
    (d)方法返回地址
    返回方法被調(diào)用的位置
    (e)一些額外的附加信息
    規(guī)范里沒有提到的信息,例如與調(diào)試相關(guān)的信息

  3. 本地方法棧
    線程私有;
    虛擬機(jī)使用的native的方法服務(wù)。

  4. java堆
    java堆是被所以線程共享的一塊內(nèi)存區(qū)域,啟動時候創(chuàng)建。
    所有對象實(shí)例和數(shù)據(jù)在堆上分配內(nèi)存。

  5. 方法區(qū)
    各個線程共享的內(nèi)存區(qū)域;
    存儲java虛擬機(jī)加載的類信息,訪問修飾符,方法描述,常量,靜態(tài)變量,編譯后的代碼等數(shù)據(jù)。
    (a)運(yùn)行的常量池
    class文件->動態(tài)性
    (b)直接內(nèi)存
    例如NIO基于通道和緩沖區(qū)的I/O方式調(diào)用native

[垃圾回收]

  1. 引用計數(shù)算法
    對象添加一個引用計數(shù)器,每當(dāng)有一個地方引用時候,計數(shù)器加1;引用失效時,計數(shù)器值減1;任何時候計數(shù)器的值為0,對象不再使用。
  2. 可達(dá)性分析算法
    以GC Roots對象為起始點(diǎn),當(dāng)一個對象到達(dá)GCRoots沒有任何引用鏈的時候,證明對象不可達(dá)。
    作為GCRoots對象的包括:
(a) 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象  
(b) 方法區(qū)中類靜態(tài)屬性引用的對象  
(c) 方法區(qū)中常量引用的對象  
(d) 本地方法棧中JNI(即一般說的native方法)引用的對象
  1. 標(biāo)記-清除算法
  2. 復(fù)制算法
    內(nèi)存分為一塊較大的Eden和兩塊較小的Survivor空間,每次使用Eden和一塊Survivor空間;
    回收時,將Eden和Survivor存活的對象一次性復(fù)制到另外一塊Survivor空間,清理用過的Eden和Survivor空間,默認(rèn)Eden和Survivor大小比例8:1
  3. 標(biāo)記-整理算法

[垃圾收集器]

  1. Serial收集器
    新生代采取復(fù)制算法,暫停所有用戶線程;
    老年代采取標(biāo)記整理算法,暫停用戶所以線程
  2. ParNew收集器
    Serial收集器的多線程版本
    GC多線程,新生代采取復(fù)制算法,暫停所有用戶線程;
    老年代采取標(biāo)記整理算法,暫停用戶所以線程
  3. Parallel Scavenge收集器
    CMS收集器盡可能縮短垃圾收集時用戶線程的停頓時間
    Parallel Scavenge收集器目的達(dá)到一個可控制的吞吐量(吞吐量=運(yùn)行代碼時間/(運(yùn)行代碼時間+垃圾收集時間))
  4. Serial Old收集器
  5. Parallel Old收集器
  6. CMS收集器
    以獲取最短回收停頓時間為目標(biāo)的收集器。
    采取標(biāo)記-清除算法 ->
(1)初始標(biāo)記
(2)并發(fā)標(biāo)記
(3)重新標(biāo)記
(4)并發(fā)清除
  1. G1收集器
    面向服務(wù)端應(yīng)用的垃圾收集器
    步驟 ->
(a)初始標(biāo)記
(b)并發(fā)標(biāo)記
(c)最終標(biāo)記
(d)篩選回收

[內(nèi)存分配]

-Xms20M,-Xmx20M,-Xmn10M -XX:SurvivorRatio=8,-XX:+PrintGCDetails
java堆大小為20M,不可擴(kuò)展,10M分配新生代,10M分配老年代
新生代的Eden區(qū)與一個Survivor區(qū)的空間比例為8:1
新生代的總可用空間(Eden區(qū)+1個Survivor區(qū)的總?cè)萘?
-XX:PermSize永久代
-XX:MaxPermSize永久代最大容量

存儲的是java的類信息,包括解析得到的方法、屬性、字段等等。永久帶基本不參與垃圾回收。
在jdk1.8之前,通過-XX:PermSize=64m -XX:MaxPermSize=128m來調(diào)整永久代大小,
在jdk1.8之后,永久代被移除,原本存儲在永久代的數(shù)據(jù)將存放在一個叫做元空間的本地內(nèi)存區(qū)域,
通過 -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m來調(diào)整元空間大小

[類加載的時機(jī)]

生命周期
-> 加載

(a)通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流   
(b)將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)運(yùn)行時的數(shù)據(jù)結(jié)構(gòu)  
(c)在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口

-> 驗(yàn)證

(a)文件格式驗(yàn)證  
(b)元數(shù)據(jù)驗(yàn)證  
(c)字節(jié)碼驗(yàn)證  
(d)符號引用驗(yàn)證

-> 準(zhǔn)備 -> 解析 -> 初始化 -> 使用 -> 卸載

[類加載器]

  1. 啟動類加載器(Bootstrap ClassLoader)
    負(fù)責(zé)將存放<JAVA_HOME>\lib目錄中;
    被-Xbootclasspath參數(shù)指定的路徑中(僅按照文件名識別,如rt.jar)
  2. 擴(kuò)展類加載器(Extension ClassLoader)
    負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中
    或者被java.ext.dirs系統(tǒng)變量所指定的路徑中所有的類庫
  3. 應(yīng)用類加載器(Application ClassLoader)
    負(fù)責(zé)加載用戶類路徑ClassPath上所指定的類庫
  4. 自定義類加載器 UserClassLoader

如果一個類加載器收到了類加載的請求,請求委派給父類加載器,只有當(dāng)父類反饋無法完成,子類嘗試加載。

[Java內(nèi)存模型]

Java內(nèi)存模型的主要目標(biāo)是定義程序中各個變量的訪問規(guī)則,即在虛擬機(jī)中將變量存儲到內(nèi)存和從內(nèi)存中取出變量這樣的底層細(xì)節(jié)。
主內(nèi)存主要對應(yīng)與java堆里的對象實(shí)例數(shù)據(jù)部分,而工作內(nèi)存則對應(yīng)虛擬機(jī)棧中的部分區(qū)域。
內(nèi)存間的交互

  1. lock鎖定
    作用于主內(nèi)存的變量,它把一個變量標(biāo)識為一條線程獨(dú)占的狀態(tài)。
  2. unlock(解鎖)
    作用于主內(nèi)存的變量,把一個處于鎖定狀態(tài)的變量釋放出來,釋放后才能被其他線程鎖定。
  3. read(讀取)
    作用于主內(nèi)存的變量,將一個變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存,以便隨后的load動作使用。
  4. load(載入)
    作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中。
  5. use(使用)
    作用于工作內(nèi)存的變量,它把一個變量的值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到需要使用變量的值的字節(jié)碼指令時將會執(zhí)行這個操作。
  6. assign(賦值)
    作用于工作內(nèi)存的變量,它把執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個給變量賦值的字節(jié)碼指令時執(zhí)行這個操作。
  7. store
    作用于工作內(nèi)存的變量,它把工作內(nèi)存內(nèi)存中一個變量的值傳送給主內(nèi)存中,以便隨后的write操作使用。
  8. write(寫入)
    作用于主內(nèi)存的變量,它把從store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。

volatile

  1. 此變量對所以線程可見(等線程A寫進(jìn)主內(nèi)存結(jié)束,線程B才開始讀取)
  2. 主內(nèi)存一致,工作內(nèi)存不能保持一致
  3. 禁止指令重排序優(yōu)化

內(nèi)存模型特征

  1. 原子性
  2. 可見性
    變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi)存刷新變量值這種依賴主內(nèi)存作為傳遞媒介的方式來實(shí)現(xiàn)可見性。
    volatile的特殊規(guī)則保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新。
  3. 有序性

[線程的實(shí)現(xiàn)]

  1. 使用內(nèi)核線程實(shí)現(xiàn)
    內(nèi)核通過操縱調(diào)度器對線程進(jìn)行調(diào)度,并將線程的任務(wù)映射到各個處理器上。
    內(nèi)核線程一種高級接口--輕量級進(jìn)程LWP,每個輕量級進(jìn)程由一個內(nèi)核線程支持。
    用戶態(tài)和內(nèi)核態(tài)中來回切換。
    1:1
  2. 使用用戶線程實(shí)現(xiàn)
    1:N
  3. 使用用戶線程實(shí)現(xiàn)和使用用戶線程加輕量級進(jìn)程混合實(shí)現(xiàn)
    N:M

[線程安全的同步方法]

  1. 互斥同步
    互斥是實(shí)現(xiàn)同步的一種手段,臨界區(qū),互斥量,信號量
    阻塞同步
  2. 非阻塞同步
(a)測試并設(shè)置
(b)獲取并增加
(c)交換
(d)比較并交換
(e)加載鏈接/條件存儲
  1. 無同步方案
    不涉及共享數(shù)據(jù),不依賴堆的數(shù)據(jù)和公共系統(tǒng)資源

[鎖優(yōu)化]

  1. 自旋鎖和自適應(yīng)自旋
    自旋--線程執(zhí)行一個忙循環(huán)-XX:+UseSpinning參數(shù)來開啟
    自旋超過一定次數(shù)沒有成功獲得鎖,便使用傳統(tǒng)方式掛起線程,自旋次數(shù)默認(rèn)是10次,用戶可以使用參數(shù) -XX:PreBlockSpin更改
  2. 自適應(yīng)的自旋
    由前一次在同一個鎖上的自旋時間時間及鎖的擁有者的狀態(tài)來決定,
    如果同一個鎖對象,自旋剛剛成功過,允許自旋更長時間,很少成功,則省略自旋這個過程。
  3. 鎖消除
    虛擬機(jī)及時編譯器運(yùn)行的時候,對一些代碼要求同步,但是不可能存在數(shù)據(jù)競爭的進(jìn)行消除。
  4. 鎖粗化
    虛擬機(jī)探測到一串零碎的操作對同一個對象枷鎖,會把加鎖同步到整個操作序列外部。
  5. 輕量級鎖
    無競爭的情況下使用CAS操作去消除同步使用的互斥量
  6. 偏向鎖
    無競爭的情況下把整個同步都消除掉,CAS操作不進(jìn)行。
    -XX:+UseBiasedLocking
    鎖對象第一次被線程獲取,會把對象頭標(biāo)志設(shè)置為“01”,即偏向模式,同時用CAS操作記錄在對象的Mark Word之中,
    如果CAS成功,以后每次持有偏向鎖的線程都不再進(jìn)行任何同步操作。
    如果有另一個線程來獲取,偏向鎖結(jié)束,根據(jù)鎖對象是否處于被鎖定的狀態(tài),撤銷偏向,后續(xù)操作和輕量級鎖執(zhí)行類似。
    缺點(diǎn):多個線程下訪問,并不適合。

本文參考于《深入理解Java虛擬機(jī)》推薦大家看一看,會對自己收獲很大。

最后編輯于
?著作權(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)容

  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,899評論 0 11
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,816評論 11 349
  • 一、運(yùn)行時數(shù)據(jù)區(qū)域 Java虛擬機(jī)管理的內(nèi)存包括幾個運(yùn)行時數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計數(shù)器,...
    加油小杜閱讀 1,588評論 1 15
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時不同JDK版本的...
    高廣超閱讀 16,056評論 3 83
  • 既是來學(xué)習(xí)的,就先從自然拼讀法說起。 什么是自然拼讀法? 先用自己的話來解釋一下:26個字母除了我們所熟知的字母名...
    SophieXiong閱讀 623評論 0 1

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