什么是jvm?

轉(zhuǎn)自 http://blog.csdn.net/stanlee_0/article/details/51171382
說明:做java開發(fā)的幾乎都知道jvm這個(gè)名詞,但是由于jvm對(duì)實(shí)際的簡(jiǎn)單開發(fā)的來說關(guān)聯(lián)的還是不多,一般工作個(gè)一兩年(當(dāng)然不包括愛學(xué)習(xí)的及專門做性能優(yōu)化的什么的),很少有人能很好的去學(xué)習(xí)及理解什么是jvm,以及弄清楚jvm的工作原理,個(gè)人認(rèn)為這塊還是非常有必要去認(rèn)真了解及學(xué)習(xí)的,特別是剛?cè)腴T或入門不久的java開發(fā)來說,這是java的基石。

JVM(Java Virtual Machine,Java虛擬機(jī))

Java程序的跨平臺(tái)特性主要是指字節(jié)碼文件可以在任何具有Java虛擬機(jī)的計(jì)算機(jī)或者電子設(shè)備上運(yùn)行,Java虛擬機(jī)中的Java解釋器負(fù)責(zé)將字節(jié)碼文件解釋成為特定的機(jī)器碼進(jìn)行運(yùn)行。因此在運(yùn)行時(shí),Java源程序需要通過編譯器編譯成為.class文件。眾所周知java.exe是java class文件的執(zhí)行程序,但實(shí)際上java.exe程序只是一個(gè)執(zhí)行的外殼,它會(huì)裝載jvm.dll(windows下,下皆以windows平臺(tái)為例,linux下和solaris下其實(shí)類似,為:libjvm.so),這個(gè)動(dòng)態(tài)連接庫(kù)才是java虛擬機(jī)的實(shí)際操作處理所在。

JVM是JRE的一部分。它是一個(gè)虛構(gòu)出來的計(jì)算機(jī),是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的。JVM有自己完善的硬件架構(gòu),如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。Java語言最重要的特點(diǎn)就是跨平臺(tái)運(yùn)行。使用JVM就是為了支持與操作系統(tǒng)無關(guān),實(shí)現(xiàn)跨平臺(tái)。所以,JAVA虛擬機(jī)JVM是屬于JRE的,而現(xiàn)在我們安裝JDK時(shí)也附帶安裝了JRE(當(dāng)然也可以單獨(dú)安裝JRE)。

JVM內(nèi)存區(qū)域劃分

粗略分來,JVM的內(nèi)部體系結(jié)構(gòu)分為三部分,分別是:類裝載器(ClassLoader)子系統(tǒng),運(yùn)行時(shí)數(shù)據(jù)區(qū),和執(zhí)行引擎。

類裝載器

每一個(gè)Java虛擬機(jī)都由一個(gè)類加載器子系統(tǒng)(class loader subsystem),負(fù)責(zé)加載程序中的類型(類和接口),并賦予唯一的名字。每一個(gè)Java虛擬機(jī)都有一個(gè)執(zhí)行引擎(execution engine)負(fù)責(zé)執(zhí)行被加載類中包含的指令。JVM的兩種類裝載器包括:?jiǎn)?dòng)類裝載器和用戶自定義類裝載器,啟動(dòng)類裝載器是JVM實(shí)現(xiàn)的一部分,用戶自定義類裝載器則是Java程序的一部分,必須是ClassLoader類的子類。

執(zhí)行引擎:它或者在執(zhí)行字節(jié)碼,或者執(zhí)行本地方法

主要的執(zhí)行技術(shù)有:解釋,即時(shí)編譯,自適應(yīng)優(yōu)化、芯片級(jí)直接執(zhí)行其中解釋屬于第一代JVM,即時(shí)編譯JIT屬于第二代JVM,自適應(yīng)優(yōu)化(目前Sun的HotspotJVM采用這種技術(shù))則吸取第一代JVM和第二代JVM的經(jīng)驗(yàn),采用兩者結(jié)合的方式 。

自適應(yīng)優(yōu)化:開始對(duì)所有的代碼都采取解釋執(zhí)行的方式,并監(jiān)視代碼執(zhí)行情況,然后對(duì)那些經(jīng)常調(diào)用的方法啟動(dòng)一個(gè)后臺(tái)線程,將其編譯為本地代碼,并進(jìn)行仔細(xì)優(yōu)化。若方法不再頻繁使用,則取消編譯過的代碼,仍對(duì)其進(jìn)行解釋執(zhí)行。

運(yùn)行時(shí)數(shù)據(jù)區(qū):主要包括:方法區(qū),堆,Java棧,PC寄存器,本地方法棧

jvm結(jié)構(gòu)
  • 方法區(qū)和堆由所有線程共享

    堆:存放所有程序在運(yùn)行時(shí)創(chuàng)建的對(duì)象

    方法區(qū):當(dāng)JVM的類裝載器加載.class文件,并進(jìn)行解析,把解析的類型信息放入方法區(qū)。

  • Java棧和PC寄存器由線程獨(dú)享

    JVM棧是線程私有的,每個(gè)線程創(chuàng)建的同時(shí)都會(huì)創(chuàng)建JVM棧,JVM棧中存放的為當(dāng)前線程中局部基本類型的變量(java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double)、部分的返回結(jié)果以及Stack Frame,非基本類型的對(duì)象在JVM棧上僅存放一個(gè)指向堆上的地址

  • 本地方法棧:存儲(chǔ)本地方法調(diào)用的狀態(tài)

JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

因?yàn)閖vm運(yùn)行時(shí)的數(shù)據(jù)區(qū)對(duì)我們開發(fā)來說還是特別重要要掌握的知識(shí)所以單拎開來西說下。
  • 方法區(qū)域(Method Area)

    在Sun JDK中這塊區(qū)域?qū)?yīng)的為PermanetGeneration,又稱為持久代。

    方法區(qū)域存放了所加載的類的信息(名稱、修飾符等)、類中的靜態(tài)變量、類中定義為final類型的常量、類中的Field信息、類中的方法信息,當(dāng)開發(fā)人員在程序中通過Class對(duì)象中的getName、isInterface等方法來獲取信息時(shí),這些數(shù)據(jù)都來源于方法區(qū)域,同時(shí)方法區(qū)域也是全局共享的,在一定的條件下它也會(huì)被GC,當(dāng)方法區(qū)域需要使用的內(nèi)存超過其允許的大小時(shí),會(huì)拋出OutOfMemory的錯(cuò)誤信息。

  • 堆(Heap)

    它是JVM用來存儲(chǔ)對(duì)象實(shí)例以及數(shù)組值的區(qū)域,可以認(rèn)為Java中所有通過new創(chuàng)建的對(duì)象的內(nèi)存都在此分配,Heap中的對(duì)象的內(nèi)存需要等待GC進(jìn)行回收。

    堆是JVM中所有線程共享的,因此在其上進(jìn)行對(duì)象內(nèi)存的分配均需要進(jìn)行加鎖,這也導(dǎo)致了new對(duì)象的開銷是比較大的

    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ì)象過大的話則仍然是直接使用堆空間分配

    TLAB僅作用于新生代的Eden Space,因此在編寫Java程序時(shí),通常多個(gè)小的對(duì)象比大的對(duì)象分配起來更加高效。

  • JavaStack(java的棧):虛擬機(jī)只會(huì)直接對(duì)Javastack執(zhí)行兩種操作:以幀為單位的壓?;虺鰲?/p>

    每個(gè)幀代表一個(gè)方法,Java方法有兩種返回方式,return和拋出異常,兩種方式都會(huì)導(dǎo)致該方法對(duì)應(yīng)的幀出棧和釋放內(nèi)存。

    幀的組成:局部變量區(qū)(包括方法參數(shù)和局部變量,對(duì)于instance方法,還要首先保存this類型,其中方法參數(shù)按照聲明順序嚴(yán)格放置,局部變量可以任意放置),操作數(shù)棧,幀數(shù)據(jù)區(qū)(用來幫助支持常量池的解析,正常方法返回和異常處理)。

  • ProgramCounter(程序計(jì)數(shù)器)

    每一個(gè)線程都有它自己的PC寄存器,也是該線程啟動(dòng)時(shí)創(chuàng)建的。PC寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的餓地址,這里的地址可以是一個(gè)本地指針,也可以是在方法區(qū)中相對(duì)應(yīng)于該方法起始指令的偏移量。

    若thread執(zhí)行Java方法,則PC保存下一條執(zhí)行指令的地址。若thread執(zhí)行native方法,則Pc的值為undefined

  • Nativemethodstack(本地方法棧):保存native方法進(jìn)入?yún)^(qū)域的地址

    依賴于本地方法的實(shí)現(xiàn),如某個(gè)JVM實(shí)現(xiàn)的本地方法借口使用C連接模型,則本地方法棧就是C棧,可以說某線程在調(diào)用本地方法時(shí),就進(jìn)入了一個(gè)不受JVM限制的領(lǐng)域,也就是JVM可以利用本地方法來動(dòng)態(tài)擴(kuò)展本身。

JVM垃圾回收

Sun的JVMGenerationalCollecting(垃圾回收)原理是這樣的:把對(duì)象分為年青代(Young)、年老代(Tenured)、持久代(Perm),對(duì)不同生命周期的對(duì)象使用不同的算法。(基于對(duì)對(duì)象生命周期分析)

**通常我們說的JVM內(nèi)存回收總是在指堆內(nèi)存回收,確實(shí)只有堆中的內(nèi)容是動(dòng)態(tài)申請(qǐng)分配的,所以以上對(duì)象的年輕代和年老代都是指的JVM的Heap空間,而持久代則是之前提到的MethodArea,不屬于Heap。**

GC的基本原理:將內(nèi)存中不再被使用的對(duì)象進(jìn)行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時(shí)間,Java在對(duì)對(duì)象的生命周期特征進(jìn)行分析后,按照新生代、舊生代的方式來對(duì)對(duì)象進(jìn)行收集,以盡可能的縮短GC對(duì)應(yīng)用造成的暫停

(1)對(duì)新生代的對(duì)象的收集稱為minor GC;

(2)對(duì)舊生代的對(duì)象的收集稱為Full GC;

(3)程序中主動(dòng)調(diào)用System.gc()強(qiáng)制執(zhí)行的GC為Full GC。

不同的對(duì)象引用類型, GC會(huì)采用不同的方法進(jìn)行回收,JVM對(duì)象的引用分為了四種類型:

(1)強(qiáng)引用:默認(rèn)情況下,對(duì)象采用的均為強(qiáng)引用(這個(gè)對(duì)象的實(shí)例沒有其他對(duì)象引用,GC時(shí)才會(huì)被回收)

(2)軟引用:軟引用是Java中提供的一種比較適合于緩存場(chǎng)景的應(yīng)用(只有在內(nèi)存不夠用的情況下才會(huì)被GC)

(3)弱引用:在GC時(shí)一定會(huì)被GC回收

(4)虛引用:由于虛引用只是用來得知對(duì)象是否被GC

  • Young(年輕代)

    年輕代分三個(gè)區(qū)。一個(gè)Eden區(qū),兩個(gè)Survivor區(qū)。大部分對(duì)象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時(shí),還存活的對(duì)象將被復(fù)制到Survivor區(qū)(兩個(gè)中的一個(gè)),當(dāng)這個(gè)Survivor區(qū)滿時(shí),此區(qū)的存活對(duì)象將被復(fù)制到另外一個(gè)Survivor區(qū),當(dāng)這個(gè)Survivor去也滿了的時(shí)候,從第一個(gè)Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對(duì)象,將被復(fù)制年老區(qū)(Tenured。需要注意,Survivor的兩個(gè)區(qū)是對(duì)稱的,沒先后關(guān)系,所以同一個(gè)區(qū)中可能同時(shí)存在從Eden復(fù)制過來對(duì)象,和從前一個(gè)Survivor復(fù)制過來的對(duì)象,而復(fù)制到年老區(qū)的只有從第一個(gè)Survivor去過來的對(duì)象。而且,Survivor區(qū)總有一個(gè)是空的。

  • Tenured(年老代)

    年老代存放從年輕代存活的對(duì)象。一般來說年老代存放的都是生命期較長(zhǎng)的對(duì)象。

  • Perm(持久代)

    用于存放靜態(tài)文件,如今Java類、方法等。持久代對(duì)垃圾回收沒有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class,例如Hibernate等,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來存放這些運(yùn)行過程中新增的類。持久代大小通過-XX:MaxPermSize=進(jìn)行設(shè)置。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JVM內(nèi)存模型Java虛擬機(jī)(Java Virtual Machine=JVM)的內(nèi)存空間分為五個(gè)部分,分別是: ...
    光劍書架上的書閱讀 2,782評(píng)論 2 26
  • 內(nèi)存溢出和內(nèi)存泄漏的區(qū)別 內(nèi)存溢出:out of memory,是指程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,...
    Aimerwhy閱讀 808評(píng)論 0 1
  • 原文閱讀 前言 這段時(shí)間懈怠了,罪過! 最近看到有同事也開始用上了微信公眾號(hào)寫博客了,挺好的~給他們點(diǎn)贊,這博客我...
    碼農(nóng)戲碼閱讀 6,161評(píng)論 2 31
  • 12.25日精進(jìn):敬畏—進(jìn)入—體驗(yàn)—交給—持續(xù) 1,缺啥補(bǔ)啥,怕啥練啥; 2,一切為我所用,所用為團(tuán)隊(duì)家; 3,...
    京心達(dá)畢玉娜閱讀 143評(píng)論 0 0
  • 蘇軾在《減字木蘭花?立春》中寫到:春牛春杖。無限春風(fēng)來海上。便與春工。染得桃紅似肉紅。春幡春勝。一陣春風(fēng)吹酒醒。...
    俊達(dá)Dean閱讀 343評(píng)論 0 1

友情鏈接更多精彩內(nèi)容