這篇文章將為大家詳細講解有關(guān)深入淺析Java中 JVM的原理,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的。Java虛擬機包括一套字節(jié)碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域。 JVM屏蔽了與具體操作系統(tǒng)平臺相關(guān)的信息,使Java程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行。是運行Java應用最底層部分。
JDK(Java Development kit)
整個Java的核心,包括了Java運行環(huán)境(Java Runtime Envirnment),一堆Java工具(編譯,debug等)和Java基礎(chǔ)的類庫(rt.jar)。是開發(fā)java應用的基礎(chǔ)。
JRE(Java Runtime Environment,Java運行環(huán)境)
運行JAVA程序所必須的環(huán)境的集合,包含JVM標準實現(xiàn)及Java核心類庫。運行java應用的基礎(chǔ)。
J2SE(Java 2 Platform,Standard Edition)。
包含那些構(gòu)成Java語言核心的類。比如:數(shù)據(jù)庫連接、接口定義、輸入/輸出、網(wǎng)絡編程
J2EE(Java 2 Platform,Enterprise Edition)。
Enterprise Edition(企業(yè)版) J2EE 包含J2SE 中的類,并且還包含用于開發(fā)企業(yè)級應用的類。比如:EJB、servlet、JSP、XML、事務控制。
主要JVM
首先,JVM是一套規(guī)范。很多公司均實現(xiàn)了各自的虛擬機。常見的有
HotSpot JVM(sun)
Jrockit JVM(BEA公司的JVM,應用于weblogic)
IBM JVM
Apache Harmony
其中,我們常用的是HotSpot JVM.
JVM結(jié)構(gòu)

第一步(編譯):
創(chuàng)建完源文件之后,程序會先被編譯為.class文件。Java編譯一個類時,如果這個類所依賴的類還沒有被編譯,編譯器就會先編譯這個被依賴的類,然后 引用,這個有點象make。如果java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話 報“cant find symbol”的錯誤。
第二步(運行):
Java類運行的過程大概可分為兩個過程:1、類的加載 2、類的執(zhí)行。
需要說明的是:JVM主要在程序第一次主動使用類的時候,才會去加載該類。也就是說,JVM并不是在一開始就把一個程序就所有的類都加載到內(nèi)存中,而是到不得不用的時候才把它加載進來,而且只加載一次。
1、 在 編譯好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系統(tǒng)就會啟動一個jvm進程,jvm進程從classpath路徑中找到一個名為AppMain.class的二進制文件,將 MainApp的類信息加載到運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),這個過程叫做MainApp類的加載。
2、 (java命令)然后JVM找到AppMain的主函數(shù)入口,開始執(zhí)行main函數(shù)
3、 (類加載器)執(zhí)行過程中,會創(chuàng)建對象。JVM會首先從方法區(qū)加載類信息和相關(guān)常量,class加載完畢之后,在堆上為對象分配內(nèi)存,然后調(diào)用初始化實例,當然這時候?qū)嵗3种赶騝lass類型信息,這個信息保存在方法區(qū)中。
4、 (執(zhí)行引擎)調(diào)用實例方法時,會根據(jù)引用找到對象信息,進而可定位對應的class類型信息,和方法表。
5、 (執(zhí)行引擎)執(zhí)行方法時,在虛擬機棧中進行,分配棧幀,隨著入棧出棧,完成方法調(diào)用操作。
執(zhí)行引擎
運行Java的每一個線程都是一個獨立的虛擬機執(zhí)行引擎的實例。從線程生命周期的開始到結(jié)束,他要么在執(zhí)行字節(jié)碼,要么在執(zhí)行本地方法。一個線程可能通過解釋或者使用芯片級指令直接執(zhí)行字節(jié)碼,或者間接通過JIT執(zhí)行編譯過的本地代碼。我們上文講到的main函數(shù),也就是執(zhí)行引擎的操作入口。
Class文件
實際上,Class文件中方法的字節(jié)碼流就是有JVM的指令序列構(gòu)成的。每一條指令包含一個單字節(jié)的操作碼,后面跟隨0個或多個操作數(shù)。
iload_0 // 把存儲在局部變量區(qū)中索引為0的整數(shù)壓入操作數(shù)棧。
iload_1 // 把存儲在局部變量區(qū)中索引為1的整數(shù)壓入操作數(shù)棧。
iadd // 從操作數(shù)棧中彈出兩個整數(shù)相加,在將結(jié)果壓入操作數(shù)棧。
istore_2 // 從操作數(shù)棧中彈出結(jié)果
JVM運行時數(shù)據(jù)區(qū)
1)程序計數(shù)器(線程私有)
當前線程所執(zhí)行的字節(jié)碼的行號指示器,通過改變這個計數(shù)器的值,確定下一條要執(zhí)行的命令。分支,循環(huán),跳轉(zhuǎn)都需要它的支持。
它是線程私有的,每個線程都有專屬于自己的程序記數(shù)器,線程之間互不影響,獨立存儲,保證了線程切換后,可以恢復到原先執(zhí)行位置。
2)Java虛擬機棧(線程私有)
每個方法的執(zhí)行,同時都會在虛擬機棧上創(chuàng)建一個棧幀。用于存儲局部變量表,操作數(shù)棧,方法出口,動態(tài)鏈接等。一個方法的執(zhí)行周期,同時也就對應著棧幀的出棧入棧操作。有時候方法的遞歸,會造成大量的棧幀,達到一定的深度,會報StackOverflowError異常。有一點需要說明:在編譯器編譯Java代碼時,就已經(jīng)在字節(jié)碼中為每個方法都設(shè)置好了局部變量區(qū)和操作數(shù)棧的數(shù)據(jù)和大小。并在JVM首次加載方法所屬的Class文件時, 就將這些數(shù)據(jù)放進了方法區(qū)。因此在線程調(diào)用方法時,只需要根據(jù)方法區(qū)中的局部變量區(qū)和操作數(shù)棧的大小來分配一個新的棧幀的內(nèi)存大小,并堆入Java棧。
局部變量區(qū): 用來存放方法中的所有局部變量值,包括傳遞的參數(shù)。這些數(shù)據(jù)會被組織成以一個字長(32bit或64bit)為單位的數(shù)組結(jié)構(gòu)(以索引0開始)中。其中類 型為int, float, reference(引用類型,記錄對象在堆中地址)和returnAddress(一種JVM內(nèi)部使用的基本類型)的值占用1個字長,而byte, char和shot會擴大成1個字長存儲,long,double則使用2個字長。
操作數(shù)棧: 用來在執(zhí)行指令的時候存儲和使用中間結(jié)果數(shù)據(jù)。
幀數(shù)據(jù)區(qū): 常量池的解析,正常方法返回以及異常派發(fā)機制的信息數(shù)據(jù)都存儲在其中。
3)本地方法棧(線程私有)
與Java虛擬機棧類似,只不過該區(qū)域是為native方法提供服務。
4)方法區(qū)(Perm)(線程共享)
存儲已被虛擬機加載的類信息,常量,靜態(tài)變量,即時編譯后的代碼等數(shù)據(jù)。包含運行時常量池,用于存放編譯器生成的各種字面量和符號引用,這部分內(nèi)容是在類加載后進入方法區(qū)運行時常量池中。
5)堆

堆是整個內(nèi)存數(shù)據(jù)區(qū)最負責的部分,負責對象的創(chuàng)建。同時,垃圾回收的主要工作也在于此。堆又進一步進行細分,主要是為了滿足垃圾回收。
堆的組成
Eden(伊甸園):對象創(chuàng)建的入口。
Survivor Space:用于保存在eden space內(nèi)存池中經(jīng)過垃圾回收后沒有被回收的對象,也就是“幸存還活著”的對象。
幸存者0區(qū)(Survivor 0 space)和幸存者1區(qū)(Survivor1 space):當伊甸園的空間用完時,程序又需要創(chuàng)建對象;此時JVM的垃圾回收器將對伊甸園區(qū)進行垃圾回收,將伊甸園區(qū)中的不再被其他對象所引用的對象 進行銷毀工作。同時將伊甸園中的還有其他對象引用的對象移動到幸存者0區(qū)。幸存者0區(qū)就是用于存放伊甸園垃圾回收時所幸存下來的JAVA對象。
當將伊甸園中的還有其他對象引用的對象移動到幸存者0區(qū)時,如果幸存者0區(qū)也沒有空間來存放這些對象時,JVM的垃圾回收器將對幸存者0區(qū)進行垃圾 回收處理,將幸存者0區(qū)中不在有其他對象引用的JAVA對象進行銷毀,將幸存者0區(qū)中還有其他對象引用的對象移動到幸存者1區(qū)。幸存者1區(qū)的作用就是用于 存放幸存者0區(qū)垃圾回收處理所幸存下來的JAVA對象。
Tenured :對象經(jīng)過survivor 1 space內(nèi)存池,每經(jīng)歷過一次垃圾回收,年齡就增加1,超過設(shè)定閥值后,被移入終身代,當然也包括由于擔保機制移入的對象。對于新生代和老年代,垃圾回收器對其態(tài)度不同。發(fā)生在新生代的回收頻率頻繁,大部分對象是“朝生夕死”,收集算法一般采用高效簡單的復制算法,也就是上文描述的對象轉(zhuǎn)移操作(Eden->survivor 0,survivor 0->survivor 1)。發(fā)生在該區(qū)域的垃圾回收為Young GC.對于老年代,由于大部分對象主要為存活率高的對象,垃圾回收器采用”標記-整理“算法。發(fā)生在該區(qū)域的垃圾回收為FULL GC.
堆相關(guān)參數(shù)
(影響堆空間劃分,進而會影響GC發(fā)生頻率)JVM調(diào)優(yōu)工作,主要是基于這些參數(shù),進行適當調(diào)整管理,達到調(diào)整堆內(nèi)存大小及比例大小,以滿足實際業(yè)務需求。另外還包括方法區(qū)。
-Xms:設(shè)置 Java 應用程序啟動時的初始堆大小;
-Xmx:設(shè)置 Java 應用程序能獲得的大堆大小;
-Xss:設(shè)置線程棧的大小;
-XX:MinHeapFreeRatio:設(shè)置堆空間最小空閑比例。當堆空間的空閑內(nèi)存小于這個數(shù)值時,JVM 便會擴展堆空間;
-XX:MaxHeapFreeRatio:設(shè)置堆空間的大空閑比例。當堆空間的空閑內(nèi)存大于這個數(shù)值時,便會壓縮堆空間,得到一個較小的堆;
-XX:NewSize:設(shè)置新生代的大小;
-XX:NewRatio:設(shè)置老年代與新生代的比例,它等于老年代大小除以新生代大小;
-XX:SurvivorRatio:新生代中 eden 區(qū)與 survivor 區(qū)的比例;
-XX:MaxPermSize:設(shè)置大的持久區(qū)大小;
-XX:TargetSurvivorRatio: 設(shè)置 survivor 區(qū)的可使用率。當 survivor 區(qū)的空間使用率達到這個數(shù)值時,會將對象送入老年代。
對象的生命周期
創(chuàng)建階段
1、檢查指令的參數(shù),是否能在常量池中定位到一個類的符號引用,如果是引用,判斷代表的類是否加載,解析和初始化過
2、如果沒有加載,則必須進行加載,解析和初始化
3、類加載檢查,這時候已經(jīng)知道所需內(nèi)存的大小。
4、分配內(nèi)存。從java堆中劃分一塊大小確定的內(nèi)存。支持2種方式,至于選擇哪種方式分配內(nèi)存,與java堆是否規(guī)整有關(guān)(也就是是否空間空間和使用空間相互交錯情況)。1.指針碰撞(分界點的指示器移動);2.空閑列表方式。然而,java堆是否規(guī)整,則取決于垃圾收集器的工作方式。此外,在分配內(nèi)存時還要考慮多線程情況,保證原子性。分配內(nèi)存的原子性有2種方式進行保證(CAS 和 本地線程分配緩沖-XX +/- UseTLAB)。
5)、分配內(nèi)存完成后,初始化內(nèi)存空間(初始化為0)
6、維護對象的對象頭信息。如元數(shù)據(jù)信息,哈希碼,GC分代年齡,鎖信息,類元指針。
7、調(diào)用init方法,按照程序員意愿進行初始化。
<7.1> 從超類到子類對static成員進行初始化;
<7.2> 超類成員變量按順序初始化,遞歸調(diào)用超類的構(gòu)造方法;
<7.3> 子類成員變量按順序初始化,子類構(gòu)造方法調(diào)用。
應用階段
分為強引用、軟引用、虛引用、若引用
不可視階段;
當一個對象處于不可視階段,說明我們在其他區(qū)域的代碼中已經(jīng)不可以在引用它,其強引用已經(jīng)消失,例如,本地變量超出了其可視的范圍。
不可到達階段;
處于JVM對象生命周期不可到達階段的對象,在虛擬機所管理的對象引用根集合中再也找不到直接或間接的強引用,這些對象通常是指所有線程棧中的臨時變量, 所有已裝載的類的靜態(tài)變量或者對本地代碼接口(JNI)的引用。這些對象都是要被垃圾回收器回收的預備對象,但此時該對象并不能被垃圾回收器直接回收。其 實所有垃圾回收算法所面臨的問題是相同的——找出由分配器分配的,但是用戶程序不可到達的內(nèi)存塊。
可收集階段、終結(jié)階段、釋放階段 ;
當一個對象處于可收集階段、終結(jié)階段與釋放階段時
<1> 回收器發(fā)現(xiàn)該對象已經(jīng)不可達。
<2> finalize方法已經(jīng)被執(zhí)行。
<3> 對象空間已被重用。
關(guān)于深入淺析Java中 JVM的原理就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
網(wǎng)站欄目:深入淺析Java中JVM的原理-創(chuàng)新互聯(lián)
分享地址:http://chinadenli.net/article44/dijche.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗、品牌網(wǎng)站建設(shè)、全網(wǎng)營銷推廣、ChatGPT、網(wǎng)站設(shè)計公司、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容