Java虛擬機 (JVM--Java Virtual Machine)
html
對java虛擬機的介紹文章多如牛毛,寫本文目的在於梳理一下,也方便之後翻來看看。
另外網上文章的圖都挺醜的,本文90%的圖都出於在下親筆,如圖有錯誤,請指出,定當當即更正
本文主要介紹一下Java虛擬機的抽象結構以及一些基礎的概念
複製代碼
有了JRE就能運行java程序,若是隻是運行軟件,裝個JRE就好了。
咱們通常說java8,java10都是指的JDK,是java開發者使用的工具集,是一個大的概念,下面是java8的JDK組成
複製代碼
JDK:Java 語言的軟件開發工具包(
Java Development Kit
)
JRE: Java運行時環境(Java Runtime Environment
)
JVM: Java虛擬機(Java Virtual Machine
)java
虛擬機即:模擬計算機功能,並提供統一操做接口,從而實現代碼在不一樣平臺的一致性。
從本質上來看,JVM是一個抽象接口,它有不少實現(以下),而這些實現也只是應用程序而已。
Java發展至今JVM也有過更新迭代,也有基於不一樣場景下使用的JVM。算法
Sun Classic VM 第一款商用Java 虛擬機,純解釋器方式執行java代碼。(已退出歷史舞臺)
EXact VM 編譯器解釋器混合工做,很快被HotSpot VM取代(已退出歷史舞臺)
HotSpot VM 沿用至今
KVM 手機端----效率低(已退出歷史舞臺)
JRockit 專一服務端應用
J9 IBM公司
Microsoft JVM windows----平臺專用(已退出歷史舞臺)
Taobao VM 淘寶根據HotSpot VM定製
Dalvik 安卓虛擬機,寄存器架構,執行.dex文件(.class-->.dex)
複製代碼
.class
文件和類加載器
.class
文件若是對class感興趣,能夠詳見官方文檔,對class文件介紹的很是細緻,之後會寫個專篇,這裏不深刻。
java第一天便知道javac命令能將.java生成.class文件,而後就上手IDE,基本也就與class文件無緣了windows
關於
類加載器子系統
這裏展現也不深刻,只要知道考它將java字節碼文件載入JVM便可
詳見:JVM篇2:[-加載器ClassLoader-]bash
程序計數器
當前線程私有,即每個線程都有一個,經過改變它來選取下一條須要執行的字節碼指令
內存空間較小,JVM中惟一不會出現OutOfMemoryError狀況
若是該線程正在執行一個本地方法,那麼此時程序計數器的值是「undefined」
複製代碼
Java虛擬機棧
調節參數:-Xss =
---->[1.棧幀(StatckFrame)]-------------------------------------------
|--- 一個線程的每一個方法在執行的同時,都會建立一個棧幀。
|--- 棧幀中存儲的有局部變量表、操做站、動態連接、方法出口等。
|--- 當方法被調用時,棧幀入棧,當方法執行完成時,棧幀出棧。
---->[2.其餘相關]-------------------------------------------
|--- 存取的速度快,僅次於寄存器
|--- 存儲着方法的相關局部變量,包括各類基本數據類型,對象的引用,返回地址等。
|--- 局部變量表的內存空間在編譯器完成分配,運行期不能改變其大小
---->[3.異常相關]-------------------------------------------
棧溢出: Java虛擬機會拋出StackOverflowError
內存溢出: 建立/動態擴展時沒有足夠的內存建立對應的Java虛擬機棧,拋出OutOfMemoryError異常
複製代碼
Java虛擬機棧``本地方法棧
當前線程私有,本地方法棧支持Native方法調用
HotSpot VM將本地方法棧和Java虛擬機棧合二爲一
異常狀況:StackOverflowError和OutOfMemoryError
複製代碼
關於線程私有和線程間共享,詳見:Java內存模型(JMM--Java Memory Model)架構
方法區
-XX:PermSize =
-XX:MaxPermSize =
---->[1.基本介紹]-------------------------------------------
當前線程共享區域:用於存儲已經被虛擬機加載的[類信息](即加載類時須要加載的信息,
包括版本、field、方法、接口等信息)、final常量、靜態變量、編譯器即時編譯的代碼等。
---->[2.運行時常量池]-------------------------------------------
|---用於存儲編譯期就生成的字面常量、符號引用、翻譯出來的直接引用
|---存儲在運行時產生的常量(好比String類的intern方法,做用是String維護了一個常量池,若是調用的字符「abc」已經在常量池中,則返回池中的字符串地址,不然,新建一個常量加入池中,並返回地址)
[符號引用]:編碼是用字符串表示某個變量、接口的位置,直接引用就是根據符號引用翻譯出來的地址,將在類連接階段完成翻譯
---->[3.異常相關]-------------------------------------------
異常狀況:OutOfMemoryError異常。
複製代碼
方法區、永久帶和元空間簡介
併發
方法區(Method Area):jvm規範裏面的運行時數據區的一個組成部分(接口層面)
永久帶(Perm):jdk7及以前的版本含有,是方法區的具體實現。(實現層面)
元空間(Metaspace):jdk8及以後的版本含有,是方法區的具體實現。(實現層面) # 元空間使用本地內存,不在JVM中
其實理解起來也不難:先定義和使用接口,再用具體實現完成工做。
複製代碼
Java堆
Java 堆:存放對象
|---新生代:存儲新生的對象
|--- Eden(E區):存放JVM中剛生成的Java對象
|--- Survivor(S區)
|--- FromSpace (S0)
|--- ToSpace (S1)
|---老年代:存儲長期存活的對象
|--- 年齡達標(新生代中GC下存活的次數),可經過 -XX:MaxTenuringThreshold 指定
|--- 超大對象,直接進入老年代 。
非堆:存儲程序運行時長期存活的對象,好比類的元數據、方法、常量、屬性等。
複製代碼
-XX:NewSize= 新生代大小
-XX:SurvivorRatio= E區與兩個S區的比值 默認 8:2
-XX:NewRatio= 新生代和老年代的比值
-XX:MaxTenuringThreshold 進入老年代年齡閥值
-Xms 初始堆大小 : 默認是物理內存的1/64
-Xmx 最大堆大小 : 默認是物理內存的1/4
-XX:PermSize= 非堆內存初始值: 默認是物理內存的1/64
-XX:MaxPermSize= 最大非堆內存: 默認是物理內存的1/4
堆大小:新生代 + 老年代 + 持久代
複製代碼
Minor GC : 清理新生代
Full GC : 清理整個堆空間
複製代碼
通常新建立對象進入E區,當E區內存用完後,觸發MinorGC。
存活的對象最終進入SurvivorFrom
複製代碼
From
和To
1.當E區再度裝滿
2.觸發MinorGC回收
3.存活的對象(包括From中)複製存放入To
4.To和From調換名稱
複製代碼
[1].爲何要分新生代和老年代?
|--- 對象的生存情況不一樣,使用不一樣的回收算法,優化GC性能
|--- 在新生代對象可能被頻繁建立或銷燬(朝生夕死),老年代對象回收較少
[2].Survivor區存在的意義?
提升對象進入老年代的門檻,減小FullGC 的次數(FullGC很耗時)
[3].S0和S1有什麼做用?
避免Survivor區空間的碎片化
複製代碼
垃圾對象的斷定
|---引用計數法
|---可達性分析
回收算法
|---標記清除
|---複製
|---標記整理
|---分代收集
垃圾回收器:
|---Serial
|---Parnew
|---CMS
|---G1
複製代碼
在對象中添加引用計數器,當該對象被引用時,引用計數器+1,引用失效時,計數器-1
對象循環引用會失效
複製代碼
Java中GC Root包括
:oracle
[1].虛擬機棧中的引用的對象。
[2].方法區中的類靜態屬性引用的對象。
[3].方法區中的常量引用的對象。
[4].本地方法棧(jni)即通常說的Native的引用對象。
複製代碼
一個對象和GC Root之間沒有連接,那麼該對象可回收
好比ObjC和ObjB
之間的鏈接斷開,橙色區域的對象可回收jvm
標記-清除(Mark-Sweep)
---->[方式]----------------------------------
[1].標記出全部須要存活的對象
[2].標記完成以後統一回收掉未被標記的對象。
---->[缺點]----------------------------------
[1].標記和清除效率都不高。
[2].會產生大量的不連續的內存碎片。
大量空間碎片的缺點:當程序須要爲較大對象分配內存時,沒法找到足夠的連續內存而出發GC。
複製代碼
標記-整理(Mark-Compact)
---->[方式]----------------------------------
[1].標記出全部存活的對象
[2].讓全部存活的對象都向一端移動,在移動過程當中清理掉未標記的對象
---->[優劣]----------------------------------
優:不會產生大量不連續內存碎片問題
劣:要執行較多的複製操做,效率將會變低,不適合存活率高的狀況
複製代碼
複製算法(Copy)
---->[方式]----------------------------------
[1].將可用內存按容量分紅大小相等的兩塊,每次只使用其中一塊。
[2].回收時,將內存中存活的對象複製到另外一塊內存,而後清理掉該內存空間
---->[優劣]----------------------------------
優:無空間碎片,實現簡單,運行高效
劣:可以使用的內存降爲原來一半
複製代碼
這不是什麼算法,而是根據不一樣的代來使用什麼時候的算法工具
新生代:可回收對象較多,回收率大。
|--- 複製算法,高效,無碎片,從E區到S區。
老年代:可回收對象少,回收率低。
|--- 標記-整理算法,無碎片。
複製代碼
---->[新生代回收器:minor GC]----------------------------
[1].Serial(串行GC)-複製
[2].ParNew(並行GC)-複製
[3].Parallel Scavenge(並行回收GC)-複製
---->[老年代回收器:full GC]----------------------------
[4].Serial Old(MSC)(串行GC)-標記-整理
[5].CMS(併發GC)-標記-清除
[6].Parallel Old(並行GC)--標記-整理
---->[G1獨立完成]-------------------------------
[7].G1(JDK1.7+)
複製代碼
好了,綜述就到這裏,具體詳情可見接下來的各篇專題。