前言:jvm是java的底層,是全部java框架的底層,凡事需深刻現象看本質,知曉其原理才能更好的作上層開發。java
1、jvm的體系結構python
類裝載器classloader:用來裝載.class文件算法
執行引擎:執行字節碼,或者執行本地方法c#
運行時數據區(這個就是本章討論的重點):方法區、虛擬機棧、本地方法棧、堆、程序計數器 數組
2、運行時數據區緩存
一、程序計數器:能夠當作當前線程所執行的字節碼的行號指示器( 線程私有)多線程
二、 java虛擬機棧:存放基本數據類型、對象引用類型(線程私有)併發
三、本地方法棧:存放本地(native)方法相關信息框架
四、方法區:存放已被虛擬機加載的類型信息、常量、靜態變量、及時編譯器編譯後額代碼緩存等數據jvm
五、堆:對象實例及數組
3、對象的內存佈局
對象在堆內存的存儲佈局能夠分爲三個部分:對象頭、實例數據、填充數據(保證對象大小都是8字節的整數倍)
Hotspot虛擬機對象的對象頭中包括兩類信息:第一類是用於存儲對象自身的運行時的數據,如hashcode、gc分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程id、偏向時間戳,這些被稱爲Mark Word,另外一類是類型指針(經過其判斷是哪一個類的實例)
4、垃圾收集(GC)
顯然,不能全部對象一直在內存中存在,這樣不停有新對象,內存就會爆掉,全部須要進行GC,而在進行GC的時候,咱們首先就須要判斷哪些對象存活,哪些對象已死,下面就介紹常見的2種算法判斷對象已死
一、引用計數算法(python)
在對象中添加一個引用計數器,每當有一個地方引用它時,計數器就加一;當引用失效時,計數器就減一;任什麼時候刻計數器爲零的對象就是不可能再被使用的(這個算法看似很好,實則否則,當出現對象循環引用時就很難解決)
二、可達性分析(java、c#)
經過一系列稱爲"GC Roots"的根對象做爲起始節點,根據其引用關係向下搜索,搜索過程所走過的路徑被稱爲「引用鏈」,當一個對象到GC Roots之間沒有任何引用鏈,可斷定爲可回收對象,不過這個時候不會直接回收掉,需進行第一次標記,隨後再經過一次篩選,篩選的條件就是此對象是否有必要執行finalize(),加入對象沒有覆蓋finalize(),或者已經執行過一次,這認爲此對象能夠被回收了。
常見的GC Roots:當前或者的線程所引用的對象、方法區的靜態變量、常量
三、引用的分類
引用分爲如下四類:強引用、軟引用、弱引用、虛引用
其中強引用就是咱們普通的new對象,只要強引用關係還存在,就不會被回收
軟引用,被軟引用的對象在系統進行下下次GC則回收
弱引用,被弱引用的對象在系統進行下次GC則回收
虛引用,徹底無影響對象生存時間,GC時會收到系統通知,就是一個對象存活的監控
5、垃圾收集算法
一、標記-清除算法
首先標記出須要回收的對象,在標記完成後,同一回收(顯然會效率低、內存碎片化)
二、標記-複製算法
將可用內存劃分爲等大小2塊,當一塊快用完了,將其或者的對象按順序移到另外一塊上(顯然浪費空間)
三、標記-整理算法
首先標記全部可回收對象,而後將其存活的對象都放一塊兒,其餘位置清掉
四、分代收集算法
分代是基於把堆分紅如下3個部分的
新生代:通常狀況下,全部新生成的對象首先放在新生代,新生代又分爲Eden區域和兩個Survivor(survivor0、suirvivor1)區,大部分對象在Eden區生成
老年代:存放的都是些生命週期較長的對象,在新生代中經歷了屢次垃圾回收仍然存活的對象就放入老年代
元空間:存放靜態文件,如java類、方法
新生代採用標記複製算法,老年代採用標記整理算法
6、經典垃圾收集器
一、Serial收集器
看其名字便知道其爲單線程收集器,單線程收集器比較大的一個問題就是,會Stop the world,就是停掉正常工做的全部線程。
serial收集器中,對新生代採用標記複製算法,對老年代採用標記整理算法
二、ParNew收集器
多線程Serial收集器
三、CMS收集器
分四個步驟初始標記(STW)、併發標記、從新標記、併發清除(STW)
缺點:併發處理、致使應用程序變慢 且CMS收集器是基於標記清除的,會形成內存碎片化
四、G1收集器
大體分四個步驟初始標記、併發標記、最終標記、篩選回收
除併發標記外,其餘步驟均STW
篩選回收:任意選擇多個Region區進行GC,將一個Region區活着的對象放入空的Region區
G1經過把堆分紅多個相對獨立的區域,並行的選擇性回收,兼顧STW與吞吐量,基於標記-複製算法