JVM是Java程序運行的環(huán)境峡继,同時是一個操作系統(tǒng)的一個應(yīng)用程序進(jìn)程匈挖,因此它有自己的生命周期,也有自己的代碼和數(shù)據(jù)空間。
JVM工作原理和特點主要是指操作系統(tǒng)裝入JVM舶吗,是通過jdk中Java.exe來完成誓琼,通過下面4步來完成JVM環(huán)境。
1.創(chuàng)建JVM裝載環(huán)境和配置
JVM裝入環(huán)境叔收,JVM提供的方式是操作系統(tǒng)的動態(tài)連接文件
2.裝載JVM.dll
通過第一步已經(jīng)找到了JVM的路徑饺律,Java通過LoadJavaVM來裝入JVM.dll文件.裝入工作很簡單就是調(diào)用Windows API函數(shù):
LoadLibrary裝載JVM.dll動態(tài)連接庫.然后把JVM.dll中的導(dǎo)出函數(shù)JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs掛接到InvocationFunctions變量的CreateJavaVM和GetDefaultJavaVMInitArgs函數(shù)指針變量上复濒。JVM.dll的裝載工作宣告完成巧颈。
3.初始化JVM.dll并掛界到JNIENV(JNI調(diào)用接口)實例
這樣就可以在Java中調(diào)用JVM的函數(shù)了.調(diào)用InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法獲得JNIEnv結(jié)構(gòu)的實例.
4.調(diào)用JNIEnv實例裝載并處理class類洛二。
JVM基本結(jié)構(gòu)
? ? ? ? JVM體系主要是兩個JVM的內(nèi)部體系結(jié)構(gòu)分為三個子系統(tǒng)和兩大組件晾嘶,分別是:類裝載器(ClassLoader)子系統(tǒng)娶吞、執(zhí)行引擎子系統(tǒng)和GC子系統(tǒng)妒蛇,組件是內(nèi)存運行數(shù)據(jù)區(qū)域和本地接口。
?
· 程序計數(shù)器(PC寄存器)
PC(Program Couneter)寄存器是每個線程私有的吏奸,Java虛擬機會為每個線程創(chuàng)建PC寄存器奋蔚,在任意時刻泊碑,一個Java線程總是在執(zhí)行一個方法馒过,這個方法稱為當(dāng)前方法,如果當(dāng)前方法不是本地方法来累,PC寄存器總會執(zhí)行當(dāng)前正在被執(zhí)行的指令佃扼,如果是本地方法蔼夜,則PC寄存器值為Underfined求冷。每執(zhí)行一條指令 PC 都會自增,因此 PC 存儲了指向下一條要被執(zhí)行的指令地址拯坟。JVM 用 PC 來跟蹤指令執(zhí)行的位置郁季,PC 將實際上是指向方法區(qū)(Method Area)的一個內(nèi)存地址钱磅。
· 方法區(qū)(永久代)
方法區(qū)存儲了每個類的信息年柠,比如類型的常量池褪迟、字段,方法信息掀抹、方法字節(jié)碼傲武。所有線程共享同一個方法區(qū)谱轨,因此訪問方法區(qū)數(shù)據(jù)的和動態(tài)鏈接的進(jìn)程必須線程安全土童。如果兩個線程試圖訪問一個還未加載的類的字段或方法工坊,必須只加載一次王污,而且兩個線程必須等它加載完畢才能繼續(xù)執(zhí)行。
(JDK1.7中尿招,已經(jīng)把放在永久代的字符串常量池移到堆中就谜。JDK1.8撤銷永久代丧荐,引入元空間喧枷。元空間是直接存在內(nèi)存中,不在java虛擬機中的车荔,因此元空間依賴于內(nèi)存大小夸赫。當(dāng)然你也可以自定義元空間大小茬腿。)
· 方法區(qū)不需要連續(xù)的內(nèi)存宜雀,可以選擇固定大小或者可擴展辐董。并且還可以選擇不實現(xiàn)垃圾收集。相對而言苔严,垃圾收集行為在這個區(qū)域是比較少出現(xiàn)的,但并非數(shù)據(jù)進(jìn)入了方法區(qū)就如永久代的名字一樣“永久”存在了欠窒。這個區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載岖妄,一般來說這個區(qū)域的回收“成績”比較難以令人滿意寂祥,尤其是類型的卸載,條件相當(dāng)苛刻福扬,但是這部分區(qū)域的回收確實是有必要的忧换。當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryError異常亚茬。
· 堆(Heap)
應(yīng)用系統(tǒng)對象都保存在Java堆中刹缝,堆被用來在運行時分配類實例梢夯、數(shù)組晴圾。不能在棧上存儲數(shù)組和對象。因為棧幀被設(shè)計為創(chuàng)建以后無法調(diào)整大小人乓。棧幀只存儲指向堆中對象或數(shù)組的引用色罚。與局部變量數(shù)組(每個棧幀中的)中的原始類型和引用類型不同账劲,對象總是存儲在堆上以便在方法結(jié)束時不會被移除金抡。對象只能由垃圾回收器移除梗肝。所有線程共享Java堆巫击。
簡述垃圾回收
為了支持分代垃圾回收機制柄粹,堆內(nèi)存可以劃分為新生代和老年代兩個區(qū)域(默認(rèn)新生代與老年代的空間大小為1:2)匆绣。新生代可以再劃分為Eden區(qū)崎淳、From Survivor區(qū)和To Survivor區(qū)(三者比例為8:1:1)。幾乎所有的新對象的創(chuàng)建都是在Eden區(qū)進(jìn)行的森爽。在垃圾回收(GC)過程中嚣镜,Eden中的活躍對象會被轉(zhuǎn)移到Survivor區(qū)菊匿,當(dāng)再到達(dá)一定的年齡(經(jīng)歷過的Minor GC的次數(shù)跌捆,每經(jīng)過一次新生代回收,如果對象存活則它的年齡就加1姆钉,對象達(dá)到一定的年齡后)抄瓦,會被轉(zhuǎn)移到老年代中钙姊。
· Java棧(Java Stack)
Java棧是線程私有的內(nèi)存區(qū)域摸恍,其中存儲的是棧幀赤屋。类早,每一次方法調(diào)用創(chuàng)建一個幀嗜逻,并壓棧,調(diào)用完畢出棧逆日。下面是內(nèi)存的線程公有私有示意圖:
如果方法methodOne方法調(diào)用了methodTwo室抽,那么methodOne就會先入棧創(chuàng)建一個棧楨坪圾,接著methodTwo再入棧成為棧頂(假設(shè)沒有其他的方法執(zhí)行)兽泄,methodTwo執(zhí)行完先出棧,接著methodOne執(zhí)行完出棧病梢。
一般由三部分組成:局部變量表蜓陌、操作數(shù)據(jù)棧和幀數(shù)據(jù)區(qū)
局部變量表:可以存放的數(shù)據(jù)有8種基本數(shù)據(jù)類型(boolean护奈,byte哥纫,char蛀骇,short擅憔,int,float蚌讼,long,double)芥喇,對象引用和returnAddress類型继控。其中l(wèi)ong和double因為是64位胖眷,會占用兩個局部變量的空間珊搀。
每一個塊就是一個棧食棕,如圖是兩個棧
在Java虛擬機規(guī)范中簿晓,對這個區(qū)域規(guī)定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度(比如遞歸調(diào)用的y時候)千埃,將拋出StackOverflowError異常;如果虛擬機椱司剩可以動態(tài)擴展(當(dāng)前大部分的Java虛擬機都可動態(tài)擴展蜈缤,只不過Java虛擬機規(guī)范中也允許固定長度的虛擬機棧)底哥,當(dāng)擴展時無法申請到足夠的內(nèi)存時會拋出OutOfMemoryError異常趾徽。
操作數(shù)棧:主要保存計算過程的中間結(jié)果孵奶,同時作為計算過程中的變量臨時的存儲空間了袁。? 下圖是一個兩數(shù)相加的操作數(shù)棧的過程:
so?Bz#+??
幀數(shù)據(jù)區(qū):除了局部變量表和操作數(shù)據(jù)棧以外载绿,棧還需要一些數(shù)據(jù)來支持常量池的解析卢鹦,這里幀數(shù)據(jù)區(qū)保存著訪問常量池的指針冀自,方便計程序訪問常量池,另外當(dāng)函數(shù)返回或出現(xiàn)異常時賣虛擬機子必須有一個異常處理表搀玖,方便發(fā)送異常的時候找到異常的代碼灌诅,因此異常處理表也是幀數(shù)據(jù)區(qū)的一部分猜拾。
棧上分配
? 小對象(一般幾十個bytes)挎袜,在沒有逃逸的情況下盯仪,可以直接分配在棧上
直接分配在棧上全景,可以自動回收爸黄,減輕GC壓力
大對象或者逃逸對象無法棧上分配
· 本地方法棧(Native Method Stack)
本地方法棧也是線程私有的內(nèi)存區(qū)域奔浅,與java棧比較相似鲁驶,不同之處在于該區(qū)域主要是保存Native方法相關(guān)的數(shù)據(jù)。Native方法是非Java語言編寫的方法舞骆。
與虛擬機棧一樣钥弯,本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryError異常径荔。
專注于Java架構(gòu)師技術(shù)分享,撩我免費送Java全套架構(gòu)師晉級資料
(Java架構(gòu)師交流企Q鵝裙*/*:445*-*820*-*908 )