- 相關(guān)推薦
Java運(yùn)行時(shí)數(shù)據(jù)區(qū)
Java具有簡(jiǎn)單性、面向?qū)ο、分布式、健壯性、安全性、平臺(tái)立與可移植性、多線程、動(dòng)態(tài)性等特點(diǎn)。下面是小編分享的Java運(yùn)行時(shí)數(shù)據(jù)區(qū),歡迎大家參考!
JVM就是一個(gè)特殊的進(jìn)程, 我們執(zhí)行的java程序, 都運(yùn)行在一個(gè)JVM進(jìn)程中, 這個(gè)進(jìn)程的作用就是加載class文件, 并且執(zhí)行class文件中的代碼。 當(dāng)然, 從一個(gè)class文件的加載, 到準(zhǔn)備好可執(zhí)行之前, 還有一段很長(zhǎng)的路要走, 以后的文章會(huì)詳細(xì)介紹這個(gè)過(guò)程。 既然虛擬機(jī)作為一個(gè)虛擬的計(jì)算機(jī), 來(lái)執(zhí)行我們的程序, 那么在執(zhí)行的過(guò)程中, 必然要有地方存放我們的代碼(class文件); 在執(zhí)行的過(guò)程中, 總會(huì)創(chuàng)建很多對(duì)象, 必須有地方存放這些對(duì)象; 在執(zhí)行的過(guò)程中, 還需要保存一些執(zhí)行的狀態(tài), 比如, 將要執(zhí)行哪個(gè)方法, 當(dāng)前方法執(zhí)行完成之后, 要返回到哪個(gè)方法等信息, 所以, 必須有一個(gè)地方來(lái)保持執(zhí)行的狀態(tài)。 上面的描述中, “地方”指的當(dāng)然就是內(nèi)存區(qū)域, 程序運(yùn)行起來(lái)之后, 就是一個(gè)動(dòng)態(tài)的過(guò)程, 必須合理的劃分內(nèi)存區(qū)域, 來(lái)存放各種數(shù)據(jù)。 所以, 在本文中, 將會(huì)詳細(xì)介紹JVM的運(yùn)行時(shí)數(shù)據(jù)區(qū)。
JVM體系結(jié)構(gòu)和運(yùn)行時(shí)數(shù)據(jù)區(qū)概述
要理解JVM的運(yùn)行時(shí)數(shù)據(jù)區(qū), 必須先要理解JVM的體系結(jié)構(gòu), 因?yàn)樘摂M機(jī)的體系結(jié)構(gòu)基本上解釋了“為什么會(huì)有這些運(yùn)行時(shí)數(shù)據(jù)區(qū)” 。 在深入理解Java虛擬機(jī)到底是什么 這篇文章中也簡(jiǎn)單的提到過(guò)JVM的體系機(jī)構(gòu), 這里再詳細(xì)的講解一下。 JVM的體系結(jié)構(gòu)如下:
由此可見(jiàn), 運(yùn)行時(shí)數(shù)據(jù)區(qū)的劃分, 是和JVM的體系結(jié)構(gòu)相關(guān)的。 本文主要介紹運(yùn)行時(shí)數(shù)據(jù)區(qū)的劃分, 對(duì)體系結(jié)構(gòu)不做深入的講解。 簡(jiǎn)單概括一下, 類(lèi)加載器子系統(tǒng)用于將class文件加載到虛擬機(jī)的運(yùn)行時(shí)數(shù)據(jù)區(qū)中(準(zhǔn)確的說(shuō)應(yīng)該是方法區(qū)) 。 可以認(rèn)為執(zhí)行引擎是字節(jié)碼的執(zhí)行機(jī)制, 一個(gè)線程可以看做是一個(gè)執(zhí)行引擎的實(shí)例。 下面介紹運(yùn)行時(shí)數(shù)據(jù)區(qū):
JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)
方法區(qū)
在字面意思上, “方法區(qū)”這個(gè)詞會(huì)讓人產(chǎn)生誤解。因?yàn)榉椒▍^(qū)存放的不只是方法, 它存放的是類(lèi)型信息。我們?cè)趯?xiě)程序的時(shí)候, 幾乎總是在和類(lèi), 對(duì)象打交道, 我們知道根據(jù)一個(gè)類(lèi)可以創(chuàng)建對(duì)象。 一般來(lái)說(shuō), 我們操縱的是對(duì)象, 訪問(wèn)對(duì)象的屬性, 調(diào)用對(duì)象的方法等, 但是我們要思考這樣一個(gè)問(wèn)題, 虛擬機(jī)根據(jù)什么信息知道如何創(chuàng)建對(duì)象的呢? 當(dāng)然是根據(jù)這個(gè)對(duì)象的類(lèi)型信息, 但是這個(gè)類(lèi)型信息在哪里呢?現(xiàn)在我們知道是在方法區(qū)中。 那么類(lèi)型信息是被誰(shuí)加載到方法區(qū)中的呢?由上面的體系結(jié)構(gòu)圖, 我們可以知道是類(lèi)加載器子系統(tǒng)?那么所謂的類(lèi)型信息, 都包含什么信息呢?這些信息又是如何存放的呢?這里的類(lèi)型信息, 可以籠統(tǒng)的認(rèn)為就是我們前面講解過(guò)的一個(gè)class文件,類(lèi)加載器子系統(tǒng)將會(huì)提取class文件里面的類(lèi)型信息,并將這些類(lèi)型信息存放到方法區(qū)中。 至于方法區(qū)中如何存放一個(gè)類(lèi)型數(shù)據(jù), 是和JVM的具體實(shí)現(xiàn)相關(guān)的。 但是不管如何實(shí)現(xiàn), 一個(gè)類(lèi)的類(lèi)型信息總是會(huì)包含如下信息:
類(lèi)的全限定名
當(dāng)前類(lèi)的直接父類(lèi)的全限定名
這個(gè)類(lèi)是接口類(lèi)型, 類(lèi)類(lèi)型, 還是枚舉類(lèi)型
類(lèi)的訪問(wèn)修飾符信息
當(dāng)前類(lèi)型的超接口的全限定名
當(dāng)前類(lèi)型的常量池
字段信息
方法信息
如果對(duì)class文件格式比較熟悉的話, 可以看出, 這些信息都是在class文件中描述過(guò)的。 由于我們無(wú)法看到類(lèi)型信息具體是如何存儲(chǔ)的, 但是大致可以將類(lèi)型信息看做一個(gè)class文件, 這有助于我們的理解。下面再次列出class文件結(jié)構(gòu)的表格,讀者可以對(duì)比class文件中的內(nèi)容到類(lèi)型數(shù)據(jù)上, 該表中的各種數(shù)據(jù)已經(jīng)在前面的博客中詳細(xì)講解過(guò):
類(lèi)型數(shù)據(jù)中,除了這些基本信息外, 類(lèi)型信息還包括以下兩個(gè)方面:
一個(gè)到類(lèi)的ClassLoader對(duì)象的引用
一個(gè)到表示該類(lèi)的Class實(shí)例對(duì)象的引用
靜態(tài)變量存儲(chǔ)區(qū)
由于之前的博客中詳細(xì)介紹過(guò)class文件的格式, 對(duì)上面的一些基本信息我們可能比較熟悉, 但是對(duì)這兩種信息就比較陌生了。 其實(shí)說(shuō)來(lái)也簡(jiǎn)單,每個(gè)class都是被一個(gè)類(lèi)加載器加載到方法區(qū)的, 類(lèi)型信息中的到類(lèi)的ClassLoader對(duì)象的引用, 表明了當(dāng)前的類(lèi)是被哪個(gè)類(lèi)加載器加載的, 這個(gè)信息同時(shí)也標(biāo)示了當(dāng)前的類(lèi)型的名稱(chēng)空間。
每當(dāng)一個(gè)class文件被成功的加載到方法區(qū)中, JVM總會(huì)創(chuàng)建一個(gè)Class對(duì)象, 來(lái)唯一標(biāo)示這個(gè)類(lèi)。 這個(gè)Class對(duì)象可以看做是類(lèi)加載過(guò)程的產(chǎn)物, 由于它描述了整個(gè)類(lèi)型信息, 而Java中的反射也是針對(duì)的類(lèi)型信息, 所以這個(gè)Class對(duì)象是反射的基石, 大多數(shù)反射API都是根據(jù)Class對(duì)象來(lái)實(shí)現(xiàn)的。
而靜態(tài)變量也是存在于類(lèi)型信息中, 可以這么說(shuō), 類(lèi)型信息中, 會(huì)有專(zhuān)門(mén)的區(qū)域存放類(lèi)的靜態(tài)變量。 與存在于對(duì)象中的實(shí)例變量不同, 靜態(tài)變量存在于類(lèi)型數(shù)據(jù)中, 每個(gè)類(lèi)型只有一份,所以也叫類(lèi)變量。
方法區(qū)是一個(gè)相對(duì)來(lái)說(shuō)比較固定的內(nèi)存區(qū), 因?yàn)樗娣诺氖穷?lèi)型信息, 而類(lèi)型信息在被加載到方法區(qū)中之后, 除了必要的連接和初始化, 一般不會(huì)有較大改動(dòng),一般情況下, JVM也不會(huì)卸載類(lèi)型信息, 所以方法區(qū)也可以稱(chēng)為JVM的靜態(tài)區(qū)。 一個(gè)類(lèi)型的生命周期一般就是整個(gè)程序的生命周期。 這也是為什么要慎用靜態(tài)變量的原因所在, 因?yàn)殪o態(tài)變量隨類(lèi)型信息存放在方法區(qū)中, 生命周期很長(zhǎng), 如果使用不當(dāng), 很容易造成內(nèi)存泄露。 一個(gè)JVM實(shí)例中只存在一個(gè)方法區(qū), 方法區(qū)中的所有類(lèi)型數(shù)據(jù)被所有線程共享。
堆
方法區(qū)是存放類(lèi)型數(shù)據(jù)的, 而堆則是存放運(yùn)行時(shí)產(chǎn)生的對(duì)象的。 和C++不同的是, Java只能在堆中存放對(duì)象, 而不能在棧上分配對(duì)象, 所有運(yùn)行時(shí)產(chǎn)生的對(duì)象全部都存放于堆中, 包括數(shù)組。 我們知道, 在Java中, 數(shù)組也是對(duì)象。一個(gè)JVM實(shí)例中只有一個(gè)堆, 所有線程共享堆中的數(shù)據(jù)(對(duì)象) 。
Java虛擬機(jī)支持幾種不同的創(chuàng)建對(duì)象的指令, 如new , anewarray等。 這些指令執(zhí)行的結(jié)果就是在堆中分配內(nèi)存, 并創(chuàng)建對(duì)象。 但是Java虛擬機(jī)的指令集中并不包含任何釋放內(nèi)存的指令, 因而我們也就不能手動(dòng)釋放內(nèi)存。 所有被創(chuàng)建的對(duì)象都會(huì)被一個(gè)叫做垃圾收集器(GC)的模塊自動(dòng)回收, 垃圾收集器有不同的實(shí)現(xiàn)方式, 他們以 特定的方式判斷對(duì)象是否過(guò)期, 并以特定的方式對(duì)對(duì)象進(jìn)行回收, 關(guān)于垃圾收集的話題不是本文的重點(diǎn), 這里就不多說(shuō)了。 我們只要知道:所有創(chuàng)建的對(duì)象都存在堆中, 而垃圾收集器會(huì)自動(dòng)回收過(guò)期的對(duì)象, 所以,JVM的堆區(qū)是垃圾收集器的“重點(diǎn)管理區(qū)” 。
Java棧
Java棧是一個(gè)線程的執(zhí)行區(qū)域, 它保存著一個(gè)線程中的方法的調(diào)用狀態(tài), 也可以說(shuō), 一個(gè)Java線程的運(yùn)行狀態(tài), 都由一個(gè)Java棧來(lái)保存。 在這個(gè)棧中, 每一方法對(duì)應(yīng)一個(gè)棧幀, 請(qǐng)注意區(qū)分棧幀和棧這兩個(gè)概念。 棧指的是整個(gè)線程的執(zhí)行棧, 棧幀是棧中的一個(gè)單位, 每個(gè)方法對(duì)應(yīng)一個(gè)棧幀。 JVM會(huì)對(duì)Java棧執(zhí)行兩種操作: 壓棧和出棧。 這兩種操作在執(zhí)行時(shí)都是以幀(棧幀)為單位的。 當(dāng)調(diào)用了一個(gè)新的方法, 就會(huì)壓入一個(gè)棧幀, 當(dāng)一個(gè)方法調(diào)用完成, 就會(huì)彈出這個(gè)方法的棧幀, 回到調(diào)用者的棧幀。
舉例來(lái)說(shuō), 如果方法a調(diào)用了方法b, 而方法b中調(diào)用了方法c。 這個(gè)過(guò)程中的方法調(diào)用和返回的裝狀態(tài)是這樣的(其中圖中兩條虛線之間表示Java棧,每個(gè)方塊表示一個(gè)特定方法的棧幀)
Java棧上的所有數(shù)據(jù)都是線程私有的, 也就是說(shuō), 每個(gè)線程都會(huì)有自己的Java棧, 不會(huì)相互訪問(wèn)其他Java棧中的數(shù)據(jù)。
PC寄存器
pc寄存器用于存放一條指令的地址, 這條指令就是虛擬機(jī)要執(zhí)行的下一條指令。pc寄存器和線程相關(guān)聯(lián), 每一個(gè)線程都有一個(gè)PC寄存器。
本地方法棧
我們知道Java可以和C/C++互調(diào)。如果當(dāng)前線程執(zhí)行的代碼是C/C++寫(xiě)的本地代碼, 那么這些方法就在本地方法棧中執(zhí)行,而不會(huì)在Java棧中執(zhí)行, Java棧中只執(zhí)行Java方法。
【Java運(yùn)行時(shí)數(shù)據(jù)區(qū)】相關(guān)文章:
Java程序運(yùn)行時(shí)數(shù)據(jù)怎么保存09-08
Java程序運(yùn)行時(shí)數(shù)據(jù)都保存到什么地方10-01
java程序運(yùn)行時(shí)內(nèi)存如何分配09-28
java數(shù)據(jù)的靜態(tài)屬性06-13
JAVA的數(shù)據(jù)權(quán)限設(shè)計(jì)05-28