1. 什么是JVM?
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫(xiě),JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。Java虛擬機(jī)包括一套字節(jié)碼指令集、一組寄存器、一個(gè)棧、一個(gè)垃圾回收堆和一個(gè)存儲(chǔ)方法域。 JVM屏蔽了與具體操作系統(tǒng)平臺(tái)相關(guān)的信息,使Java程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。JVM在執(zhí)行字節(jié)碼時(shí),實(shí)際上最終還是把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。
Java語(yǔ)言的一個(gè)非常重要的特點(diǎn)就是與平臺(tái)的無(wú)關(guān)性。而使用Java虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵。一般的高級(jí)語(yǔ)言如果要在不同的平臺(tái)上運(yùn)行,至少需要編譯成不同的目標(biāo)代碼。而引入Java語(yǔ)言虛擬機(jī)后,Java語(yǔ)言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯。Java語(yǔ)言使用Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語(yǔ)言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。Java虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。這就是Java的能夠“一次編譯,到處運(yùn)行”的原因。
2. JRE/JDK/JVM是什么關(guān)系?
JRE(JavaRuntimeEnvironment,Java運(yùn)行環(huán)境),也就是Java平臺(tái)。所有的Java 程序都要在JRE下才能運(yùn)行。普通用戶只需要運(yùn)行已開(kāi)發(fā)好的java程序,安裝JRE即可。
JDK(Java Development Kit)是程序開(kāi)發(fā)者用來(lái)來(lái)編譯、調(diào)試java程序用的開(kāi)發(fā)工具包。JDK的工具也是Java程序,也需要JRE才能運(yùn)行。為了保持JDK的獨(dú)立性和完整性,在JDK的安裝過(guò)程中,JRE也是 安裝的一部分。所以,在JDK的安裝目錄下有一個(gè)名為jre的目錄,用于存放JRE文件。
JVM(JavaVirtualMachine,Java虛擬機(jī))是JRE的一部分。它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。JVM有自己完善的硬件架構(gòu),如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。Java語(yǔ)言最重要的特點(diǎn)就是跨平臺(tái)運(yùn)行。使用JVM就是為了支持與操作系統(tǒng)無(wú)關(guān),實(shí)現(xiàn)跨平臺(tái)。
3. JVM原理
JVM是java的核心和基礎(chǔ),在java編譯器和os平臺(tái)之間的虛擬處理器。它是一種利用軟件方法實(shí)現(xiàn)的抽象的計(jì)算機(jī)基于下層的操作系統(tǒng)和硬件平臺(tái),可以在上面執(zhí)行java的字節(jié)碼程序。
java編譯器只要面向JVM,生成JVM能理解的代碼或字節(jié)碼文件。Java源文件經(jīng)編譯成字節(jié)碼程序,通過(guò)JVM將每一條指令翻譯成不同平臺(tái)機(jī)器碼,通過(guò)特定平臺(tái)運(yùn)行。
4. JVM執(zhí)行程序的過(guò)程
1) 加載.class文件 2) 管理并分配內(nèi)存 3) 執(zhí)行垃圾收集
JRE(java運(yùn)行時(shí)環(huán)境)由JVM構(gòu)造的java程序的運(yùn)行環(huán),也是Java程序運(yùn)行的環(huán)境,但是他同時(shí)一個(gè)操作系統(tǒng)的一個(gè)應(yīng)用程序一個(gè)進(jìn)程,因此他也有他自己的運(yùn)行的生命周期,也有自己的代碼和數(shù)據(jù)空間。JVM在整個(gè)jdk中處于最底層,負(fù)責(zé)于操作系統(tǒng)的交互,用來(lái)屏蔽操作系統(tǒng)環(huán)境,提供一個(gè)完整的Java運(yùn)行環(huán)境,因此也就虛擬計(jì)算機(jī)。操作系統(tǒng)裝入JVM是通過(guò)jdk中Java.exe來(lái)完成,通過(guò)下面4步來(lái)完成JVM環(huán)境:1) 創(chuàng)建JVM裝載環(huán)境和配置 2) 裝載JVM.dll 3) 初始化JVM.dll并掛界到JNIENV(JNI調(diào)用接口)實(shí)例4) 調(diào)用JNIEnv實(shí)例裝載并處理class類(lèi)。
5. JVM的生命周期
1) JVM實(shí)例對(duì)應(yīng)了一個(gè)獨(dú)立運(yùn)行的java程序它是進(jìn)程級(jí)別
a) 啟動(dòng)。啟動(dòng)一個(gè)Java程序時(shí),一個(gè)JVM實(shí)例就產(chǎn)生了,任何一個(gè)擁有public static void
main(String[] args)函數(shù)的class都可以作為JVM實(shí)例運(yùn)行的起點(diǎn)
b) 運(yùn)行。main()作為該程序初始線程的起點(diǎn),任何其他線程均由該線程啟動(dòng)。JVM內(nèi)部有兩種線程:守護(hù)線程和非守護(hù)線程,main()屬于非守護(hù)線程,守護(hù)線程通常由JVM自己使用,java程序也可以表明自己創(chuàng)建的線程是守護(hù)線程
c) 消亡。當(dāng)程序中的所有非守護(hù)線程都終止時(shí),JVM才退出;若安全管理器允許,程序也可以使用Runtime類(lèi)或者System.exit()來(lái)退出
2) JVM執(zhí)行引擎實(shí)例則對(duì)應(yīng)了屬于用戶運(yùn)行程序的線程它是線程級(jí)別的
6. JVM的體系結(jié)構(gòu)

類(lèi)裝載器(ClassLoader)(用來(lái)裝載.class文件)
執(zhí)行引擎(執(zhí)行字節(jié)碼,或者執(zhí)行本地方法)
運(yùn)行時(shí)數(shù)據(jù)區(qū)(方法區(qū)、堆、java棧、PC寄存器、本地方法棧)
7. JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

第一塊:PC寄存器
PC寄存器是用于存儲(chǔ)每個(gè)線程下一步將執(zhí)行的JVM指令,如該方法為native的,則PC寄存器中不存儲(chǔ)任何信息。
第二塊:JVM棧
JVM棧是線程私有的,每個(gè)線程創(chuàng)建的同時(shí)都會(huì)創(chuàng)建JVM棧,JVM棧中存放的為當(dāng)前線程中局部基本類(lèi)型的變量(java中定義的八種基本類(lèi)型:boolean、char、byte、short、int、long、float、double)、部分的返回結(jié)果以及Stack Frame,非基本類(lèi)型的對(duì)象在JVM棧上僅存放一個(gè)指向堆上的地址。
第三塊:堆(Heap)
它是JVM用來(lái)存儲(chǔ)對(duì)象實(shí)例以及數(shù)組值的區(qū)域,可以認(rèn)為Java中所有通過(guò)new創(chuàng)建的對(duì)象的內(nèi)存都在此分配,Heap中的對(duì)象的內(nèi)存需要等待GC進(jìn)行回收。
(1) 堆是JVM中所有線程共享的,因此在其上進(jìn)行對(duì)象內(nèi)存的分配均需要進(jìn)行加鎖,這也導(dǎo)致了new對(duì)象的開(kāi)銷(xiāo)是比較大的
(2) Sun Hotspot JVM為了提升對(duì)象內(nèi)存分配的效率,對(duì)于所創(chuàng)建的線程都會(huì)分配一塊獨(dú)立的空間TLAB(Thread Local Allocation Buffer),其大小由JVM根據(jù)運(yùn)行的情況計(jì)算而得,在TLAB上分配對(duì)象時(shí)不需要加鎖,因此JVM在給線程的對(duì)象分配內(nèi)存時(shí)會(huì)盡量的在TLAB上分配,在這種情況下JVM中分配對(duì)象內(nèi)存的性能和C基本是一樣高效的,但如果對(duì)象過(guò)大的話則仍然是直接使用堆空間分配
(3) TLAB僅作用于新生代的Eden Space,因此在編寫(xiě)Java程序時(shí),通常多個(gè)小的對(duì)象比大的對(duì)象分配起來(lái)更加高效。
(4) 所有新創(chuàng)建的Object 都將會(huì)存儲(chǔ)在新生代Yong Generation中。如果Young Generation的數(shù)據(jù)在一次或多次GC后存活下來(lái),那么將被轉(zhuǎn)移到OldGeneration。新的Object總是創(chuàng)建在Eden Space。
第四塊:方法區(qū)域(Method Area)
(1)在Sun JDK中這塊區(qū)域?qū)?yīng)的為PermanetGeneration,又稱(chēng)為持久代。
(2)方法區(qū)域存放了所加載的類(lèi)的信息(名稱(chēng)、修飾符等)、類(lèi)中的靜態(tài)變量、類(lèi)中定義為final類(lèi)型的常量、類(lèi)中的Field信息、類(lèi)中的方法信息,當(dāng)開(kāi)發(fā)人員在程序中通過(guò)Class對(duì)象中的getName、isInterface等方法來(lái)獲取信息時(shí),這些數(shù)據(jù)都來(lái)源于方法區(qū)域,同時(shí)方法區(qū)域也是全局共享的,在一定的條件下它也會(huì)被GC,當(dāng)方法區(qū)域需要使用的內(nèi)存超過(guò)其允許的大小時(shí),會(huì)拋出OutOfMemory的錯(cuò)誤信息。
第五塊:運(yùn)行時(shí)常量池(Runtime Constant Pool)
存放的為類(lèi)中的固定的常量信息、方法和Field的引用信息等,其空間從方法區(qū)域中分配。
第六塊:本地方法堆棧(Native Method Stacks)
JVM采用本地方法堆棧來(lái)支持native方法的執(zhí)行,此區(qū)域用于存儲(chǔ)每個(gè)native方法調(diào)用的狀態(tài)。
8. JVM垃圾回收

GC (Garbage Collection)的基本原理:將內(nèi)存中不再被使用的對(duì)象進(jìn)行回收,GC中用于回收的方法稱(chēng)為收集器,由于GC需要消耗一些資源和時(shí)間,Java在對(duì)對(duì)象的生命周期特征進(jìn)行分析后,按照新生代、舊生代的方式來(lái)對(duì)對(duì)象進(jìn)行收集,以盡可能的縮短GC對(duì)應(yīng)用造成的暫停
(1)對(duì)新生代的對(duì)象的收集稱(chēng)為minor GC;
(2)對(duì)舊生代的對(duì)象的收集稱(chēng)為Full GC;
(3)程序中主動(dòng)調(diào)用System.gc()強(qiáng)制執(zhí)行的GC為Full GC。
不同的對(duì)象引用類(lèi)型, GC會(huì)采用不同的方法進(jìn)行回收,JVM對(duì)象的引用分為了四種類(lèi)型:
(1)強(qiáng)引用:默認(rèn)情況下,對(duì)象采用的均為強(qiáng)引用(這個(gè)對(duì)象的實(shí)例沒(méi)有其他對(duì)象引用,GC時(shí)才會(huì)被回收)
(2)軟引用:軟引用是Java中提供的一種比較適合于緩存場(chǎng)景的應(yīng)用(只有在內(nèi)存不夠用的情況下才會(huì)被GC)
(3)弱引用:在GC時(shí)一定會(huì)被GC回收
(4)虛引用:由于虛引用只是用來(lái)得知對(duì)象是否被GC
對(duì)于JVM內(nèi)存配置參數(shù):
-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3
-Xms:初始堆大小
-Xmn:年輕代大小
-XXSurvivorRatio:年輕代中Eden區(qū)與Survivor區(qū)的大小比值
年輕代5120m, Eden:Survivor=3,Survivor區(qū)大小=1024m(Survivor區(qū)有兩個(gè),即將年輕代分為5份,每個(gè)Survivor區(qū)占一份),總大小為2048m。
-Xms初始堆大小即最小內(nèi)存值為10240m
主要內(nèi)容來(lái)自:baike.baidu.com/link