JVM是Java Virtual Machine(Java虛擬機)的縮寫,是一個虛構出來的計算機,它屏蔽了與具體操做系統平臺相關的信息,使得Java程序只需生成在Java虛擬機上運行的目標代碼(字節碼,ByteCode), 就能夠在多種平臺上不加修改地運行。這背後其實就是JVM把字節碼翻譯成具體平臺上的機器指令,從而實現「一次編寫,處處運行(Write Once, Run Anywhere)」。
Java爲何可以跨平臺?
Java引入了字節碼的概念,jvm 只能認識字節碼,並將它們解釋到系統的API調用。針對不一樣的系統有不一樣的jvm實現,有 Linux 版本的 jvm 實現,也有 Windows 版本的 jvm 實現,可是同一段代碼在編譯後的字節碼是同樣的。在不一樣的系統平臺上運行是經過JAVA解釋器將字節碼解釋爲不一樣平臺的機器碼,在不一樣的 jvm 實現上會映射到不一樣系統的 API 調用,從而實現代碼的不加修改便可跨平臺運行。java
三者的關係是:JDK>JRE>JVM算法
按照階段分爲兩個階段:
編譯階段:當咱們將一個.java的文件進行編譯,編譯程序會生成一個相同名字然後綴爲.class的文件。
運行階段主要分爲如下步驟:
安全
驗證
驗證、準備、解析這三步能夠看作是一個鏈接的過程,將類的字節碼鏈接到JVM的運行狀態之中
驗證是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,不會威脅到jvm的安全,主要包括如下幾個方面的驗證:數據結構
java public static int a=7
解析多線程
解析是將常量池內的符號引用轉爲直接引用(如物理內存地址指針)併發
初始化jvm
到了初始化階段,jvm才真正開始執行類中定義的java代碼
1)初始化階段是執行類構造器
2)當初始化一個類的時候,若是發現其父類尚未進行過初始化、則須要先觸發其父類的初始化。
3)虛擬機會保證一個類的
程序計數器開發工具
程序計數器(Progarm Counter Register)是一塊較小的內存空間,它能夠看做是當前線程所執行的字節碼行號指示器。在JVM中,經過程序計數器來記錄某個線程的字節碼執行位置,或者說記錄下一條要運行的指令。程序計數器是具有線程隔離的特性,也就是說,每一個線程工做時都有屬於本身的獨立計數器,互不影響,是一塊線程私有的內存空間。
若是當前正在執行的是一個java方法,程序計數器會記錄正在執行的java字節碼地址;若是正在執行的是native方法,則程序計數器爲空。spa
Java虛擬機棧
java虛擬機棧是線程私有的內存空間,它用來保存方法的局部變量、部分結果,並參與方法的調用和返回。
虛擬機棧在運營師採用棧幀來保存數據,棧幀中主要有局部變量表、操做數棧、動態連接地址、返回地址等信息。每個方法的調用都伴隨着棧幀的入棧操做,相應的,方法的返回則對應着棧幀的出戰操做。
和java棧相關的兩個異常:在線程的計算過程當中,若是請求的棧的深度大於最大可用的棧深度,則拋出改異常。
若是java的棧能夠擴展,在程序運行過程當中,沒有足夠的內存來支撐程序的擴展,則拋出該異常。
本地方法棧
本地方法棧和java虛擬機棧功能相似,本地方法棧主要管理本地方法棧的調用,通常是指有C實現的。和java虛擬機棧同樣會拋出StackOverFlowError和OutOfMemoryError異常
方法區
方法區是java內存區域中比較重要的一部分,主要保存的信息是元數據。其中最爲重要的是類的類型信息、常量池、域信息、方法信息。
Java堆
Java堆能夠說是Java運行時內存中最爲重要的一部分,幾乎全部的對象和數據都是在堆中分配空間的。Java堆分爲新生代和老年代兩個部分,新生代用於存放剛剛產生的對象,若是對象一直沒有被回收,生存的足夠長,老年對象就會被移入老年代。
新生代又能夠細分爲eden、surivor space0(s0或者from space)和surivor space1(s1或者To space)。eden存放剛剛建立的對象,s0和s1存放的對象至少經歷了一次垃圾回收,等倖存下來。若是倖存去的對象到了指定年齡仍未被回收,就會進入老年代。
持久代:Permanent Generation。在Sun的JVM中就是方法區的意思,儘管有些JVM大多沒有這一代。主要存放常量及類的一些信息默認最小值爲16MB,最大值爲64MB
Mark-Compact(標記-整理)算法
標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存,
分代收集算法
當前商業虛擬機的垃圾收集都採用「分代收集」(Generational Collection)算法,根據對象存活週期的不一樣將內存劃分爲幾塊並採用不用的垃圾收集算法。
通常是把 Java 堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。在新生代中,每次垃圾收集時都發現有大批對象死去,只有少許存活,那就選用複製算法,只須要付出少許存活對象的複製成本就能夠完成收集。而老年代中由於對象存活率高、沒有額外空間對它進行分配擔保,就必須使用「標記—清理」或者「標記—整理」算法來進行回收。
Serial Old收集器
cms(concurrent mark sweep)收集器
老年代收集器,致力於獲取最短回收停頓時間(即縮短垃圾回收的時間),使用標記清除算法,多線程,優勢是併發收集(用戶線程能夠和GC線程同時工做),停頓小。使用-XX:+UseConcMarkSweepGC進行ParNew+CMS+Serial Old進行內存回收,優先使用ParNew+CMS(緣由見後面),當用戶線程內存不足時,採用備用方案Serial Old收集。
初始標記階段僅僅只是標記一下 GC Roots 能直接關聯到的對象,而且修改 TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序併發運行時,能在正確可用的 Region 中建立新對象,這階段須要停頓線程,但耗時很短。
併發標記階段是從 GC Root 開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序併發執行。
而最終標記階段則是爲了修正在併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程 Remembered Set Logs 裏面,最終標記階段須要把 Remembered Set Logs 的數據合併到 Remembered Set 中,這階段須要停頓線程,可是可並行執行。
最後在篩選回收階段首先對各個 Region 的回收價值和成本進行排序,根據用戶所指望的 GC 停頓時間來制定回收計劃,從Sun公司透露出來的信息來看,這個階段其實也能夠作到與用戶程序一塊兒併發執行,可是由於只回收一部分 Region,時間是用戶可控制的,並且停頓用戶線程將大幅提升收集效率。經過下圖能夠比較清楚地看到G1收集器的運做步驟中併發和須要停頓的階段。