JVM内存区域划分及其管理机制
-
程序計(jì)數(shù)器:是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機(jī)的概念模型里(僅是概念模型,各種虛擬機(jī)可能會通過一些更高效的方式去實(shí)現(xiàn)),字 節(jié)碼解釋器工作時就是通過改變這個計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計(jì)數(shù)器來 完成。
由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實(shí)現(xiàn)的,在任何一個確定的時刻,一個處理器(對于多核處理器來說是 一個內(nèi)核)只會執(zhí)行一條線程中的指令。因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨(dú)立的程序計(jì)數(shù)器,各條線程之間的計(jì)數(shù)器互不影 響,獨(dú)立存儲,我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存。
如果線程正在執(zhí)行的是一個Java方法,這個計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是Natvie方法,這個計(jì)數(shù)器 值則為空(Undefined)。此內(nèi)存區(qū)域是唯一一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
-
方法區(qū):在類裝載器加載class文件到內(nèi)存的過程中,虛擬機(jī)會提取其中的類型信息,并將這些信息存儲到方法區(qū)。方法區(qū)用于存儲已被虛擬機(jī)加載的類信息、常量、靜 態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。由于所有線程都共享方法區(qū),因此它們對方法區(qū)數(shù)據(jù)的訪問必須被設(shè)計(jì)為是線程安全的。。在JVM規(guī)范中,沒有強(qiáng)制要求方法區(qū)必須實(shí)現(xiàn)垃圾回收。很多人習(xí)慣將方法區(qū)稱為“永久代”,是因?yàn)镠otSpot虛擬機(jī)以永久代來實(shí)現(xiàn)方法區(qū),從而JVM的垃圾收集器可以像管理堆區(qū)一樣管理這部分區(qū)域,從而不需要專門為這部分設(shè)計(jì)垃圾回收機(jī)制。垃圾回收器在回收的時候,也僅僅針對常量池和對類型的回收。不過自從JDK7之后,Hotspot虛擬機(jī)便將運(yùn)行時常量池從永久代移除了。
-
java堆:用于存儲java程序創(chuàng)建的對象實(shí)例及數(shù)組,被所有線程共享,在虛擬機(jī)啟動時創(chuàng)建,也是垃圾收集器管理的最主要區(qū)域,也稱GC堆。
-
java棧:線程私有,生命周期與線程相同。Java棧也稱作虛擬機(jī)棧(Java Vitual Machine Stack),也就是我們常常所說的棧,跟C語言的數(shù)據(jù)段中的棧類似。事實(shí)上,Java棧是Java方法執(zhí)行的內(nèi)存模型。為什么這么說呢?下面就來解釋一下其中的原因。
Java棧中存放的是一個個的棧幀,每個棧幀對應(yīng)一個被調(diào)用的方法,在棧幀中包括局部變量表(Local Variables)、操作數(shù)棧(Operand Stack)、指向當(dāng)前方法所屬的類的運(yùn)行時常量池(運(yùn)行時常量池的概念在方法區(qū)部分會談到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些額外的附加信息。當(dāng)線程執(zhí)行一個方法時,就會隨之創(chuàng)建一個對應(yīng)的棧幀,并將建立的棧幀壓棧。當(dāng)方法執(zhí)行完畢之后,便會將棧幀出棧。 因此可知,線程當(dāng)前執(zhí)行的方法所對應(yīng)的棧幀必定位于Java棧的頂部。講到這里,大家就應(yīng)該會明白為什么 在 使用 遞歸方法的時候容易導(dǎo)致棧內(nèi)存溢出的現(xiàn)象了以及為什么棧區(qū)的空間不用程序員去管理了(當(dāng)然在Java中,程序員基本不用關(guān)系到內(nèi)存分配和釋放的事情,因?yàn)?Java有自己的垃圾回收機(jī)制),這部分空間的分配和釋放都是由系統(tǒng)自動實(shí)施的。對于所有的程序設(shè)計(jì)語言來說,棧這部分空間對程序員來說是不透明的。下圖 表示了一個Java棧的模型:
局部變量表,是指存放了編譯期間可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型)和returnAddress類型(指向了一條字節(jié)碼指令的地址)。其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量的空間,其他的只占用1個。局部變量表所需的空間是在編譯時完成分配的,當(dāng)進(jìn)入一個方法時,這個方法在幀中分配多大的局部變量空間是完全確定的,因此,在方法運(yùn)行期間不會改變局部變量表的大小。
操 作數(shù)棧,想必學(xué)過數(shù)據(jù)結(jié)構(gòu)中的棧的朋友想必對表達(dá)式求值問題不會陌生,棧最典型的一個應(yīng)用就是用來對表達(dá)式求值。想想一個線程執(zhí)行方法的過程中,實(shí)際上就 是不斷執(zhí)行語句的過程,而歸根到底就是進(jìn)行計(jì)算的過程。因此可以這么說,程序中的所有計(jì)算過程都是在借助于操作數(shù)棧來完成的。
指向運(yùn)行時常量池的引用,因?yàn)樵诜椒▓?zhí)行的過程中有可能需要用到類中的常量,所以必須要有一個引用指向運(yùn)行時常量。
方法返回地址,當(dāng)一個方法執(zhí)行完畢之后,要返回之前調(diào)用它的地方,因此在棧幀中必須保存一個方法返回地址。
由于每個線程正在執(zhí)行的方法可能不同,因此每個線程都會有一個自己的Java棧,互不干擾。
-
本地方法棧:本地方法棧與Java棧的作用和原理非常相似。區(qū)別只不過是Java棧是為執(zhí)行Java方法服務(wù)的,而本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的。在JVM規(guī)范中,并沒有對本地方發(fā)展的具體實(shí)現(xiàn)方法以及數(shù)據(jù)結(jié)構(gòu)作強(qiáng)制規(guī)定,虛擬機(jī)可以自由實(shí)現(xiàn)它。在HotSopt虛擬機(jī)中直接就把本地方法棧和Java棧合二為一。
-
運(yùn)行時常量池:是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)常量池,用于存放編譯期生成的各種字面量和符號引用。
總結(jié)
以上是生活随笔為你收集整理的JVM内存区域划分及其管理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android传感器编程带实例
- 下一篇: 插件框架实现思路及原理