摘要:運(yùn)行時數(shù)據(jù)區(qū)域虛擬機(jī)在執(zhí)行的過程中會把管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。方法區(qū)的內(nèi)存收集還是會出現(xiàn),不過這個區(qū)域的內(nèi)存收集主要是針對常量池的回收和對類型的卸載。當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時將拋出異常。
運(yùn)行時數(shù)據(jù)區(qū)域
Java虛擬機(jī)在執(zhí)行Java的過程中會把管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域有各自的用途,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機(jī)進(jìn)程的啟動而存在,而有的區(qū)域則依賴線程的啟動和結(jié)束而創(chuàng)建和銷毀。
程序計數(shù)器
程序計數(shù)器是一塊較小的區(qū)域,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機(jī)的模型里,字節(jié)碼指示器就是通過改變程序計數(shù)器的值來指定下一條需要執(zhí)行的指令。分支,循環(huán)等基礎(chǔ)功能就是依賴程序計數(shù)器來完成的。
由于java虛擬機(jī)的多線程是通過輪流切換并分配處理器執(zhí)行時間來完成,一個處理器同一時間只會執(zhí)行一條線程中的指令。為了線程恢復(fù)后能夠恢復(fù)正確的執(zhí)行位置,每條線程都需要一個獨立的程序計數(shù)器,以確保線程之間互不影響。所以程序計數(shù)器是“線程私有”的內(nèi)存。
如果虛擬機(jī)正在執(zhí)行的是一個Java方法,則計數(shù)器指定的是字節(jié)碼指令對應(yīng)的地址,如果正在執(zhí)行的是一個本地方法,則計數(shù)器指定問空undefined。程序計數(shù)器區(qū)域是Java虛擬機(jī)中唯一沒有定義OutOfMemory異常的區(qū)域。
Java虛擬機(jī)棧
他和程序計數(shù)器一樣也是線程私有的,生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀用于存儲局部變量表,操作棧,動態(tài)鏈接,方法出口等信息。每一個方法被調(diào)用的過程就對應(yīng)一個棧幀在虛擬機(jī)棧中從入棧到出棧的過程。
通常所說的虛擬機(jī)運(yùn)行時分為棧和堆,這里的棧指的就是虛擬機(jī)棧或者說虛擬機(jī)棧中的局部變量表部分。
局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型、對象引用和returnAddress類型(指向一條字節(jié)碼指令的地址)。局部變量表所需的內(nèi)存空間在編譯器完成分配,當(dāng)進(jìn)入一個方法時這個方法需要在幀中分配多大的內(nèi)存空間是完全確定的,運(yùn)行期間不會改變局部變量表的大小。(64為長度的long和double會占用兩個局部變量空間,其他的數(shù)據(jù)類型占用一個)
Java虛擬機(jī)??赡艹霈F(xiàn)兩種類型的異常:1. 線程請求的棧深度大于虛擬機(jī)允許的棧深度,將拋出StackOverflowError。2.虛擬機(jī)??臻g可以動態(tài)擴(kuò)展,當(dāng)動態(tài)擴(kuò)展是無法申請到足夠的空間時,拋出OutOfMemory異常。
本地方法棧
本地方法棧和虛擬機(jī)?;绢愃?,只不過Java虛擬機(jī)棧執(zhí)行的是Java代碼(字節(jié)碼),本地方法棧中執(zhí)行的是本地方法的服務(wù)。本地方法棧中也會拋出StackOverflowError和OutOfMemory異常。
堆
堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊。堆是所有線程共享的一塊區(qū)域,在虛擬機(jī)啟動時創(chuàng)建。堆的唯一目的是存放對象實例,幾乎所有的對象實例都在這里分配,不過隨著JIT編譯器的發(fā)展和逃逸技術(shù)的成熟,棧上分配和標(biāo)量替換技術(shù)使得這種情況發(fā)生著微妙的變化,對上分配正變得不那么絕對。
附:在Java編程語言和環(huán)境中,即時編譯器(JIT compiler,just-in-time compiler)是一個把Java的字節(jié)碼(包括需要被解釋的指令的程序)轉(zhuǎn)換成可以直接發(fā)送給處理器的指令的程序。當(dāng)你寫好一個Java程序后,源語言的語句將由Java編譯器編譯成字節(jié)碼,而不是編譯成與某個特定的處理器硬件平臺對應(yīng)的指令代碼(比如,Intel的Pentium微處理器或IBM的System/390處理器)。字節(jié)碼是可以發(fā)送給任何平臺并且能在那個平臺上運(yùn)行的獨立于平臺的代碼。
Java堆是垃圾收集器管理的主要區(qū)域,所以也稱為“GC堆”。由于現(xiàn)在的垃圾收集器基本上都是采用分代收集算法,所以Java堆還可細(xì)分為:新生代和老生代。在細(xì)致一點可分為Eden空間,F(xiàn)rom Survivor空間,To Survivor空間。如果從內(nèi)存分配的角度看,線程共享的Java堆可劃分出多個線程私有的分配緩沖區(qū)。不過無論如何劃分,都與存放內(nèi)容無關(guān),無論哪個區(qū)域,都是用來存放對象實例。細(xì)分的目的是為了更好的回收內(nèi)存或者更快的分配內(nèi)存。
Java堆可以是物理上不連續(xù)的空間,只要邏輯上連續(xù)即可,主流的虛擬機(jī)都是按照可擴(kuò)展的方式來實現(xiàn)的。如果當(dāng)前堆中沒有內(nèi)存完成對象實例的創(chuàng)建,并且不能在進(jìn)行內(nèi)存擴(kuò)展,則會拋出OutOfMemory異常。
方法區(qū)
方法區(qū)也是線程共享的區(qū)域,用于存儲已經(jīng)被虛擬機(jī)加載的類信息,常量,靜態(tài)變量和即時編譯器(JIT)編譯后的代碼等數(shù)據(jù)。Java虛擬機(jī)把方法區(qū)描述為堆的一個邏輯分區(qū),不過方法區(qū)有一個別名Non-Heap(非堆),用于區(qū)別于Java堆區(qū)。
Java虛擬機(jī)規(guī)范對這個區(qū)域的限制也非常寬松,除了可以是物理不連續(xù)的空間外,也允許固定大小和擴(kuò)展性,還可以不實現(xiàn)垃圾收集。相對而言,垃圾收集行為在這個區(qū)域是比較少出現(xiàn)的(所以常量和靜態(tài)變量的定義要多注意)。方法區(qū)的內(nèi)存收集還是會出現(xiàn),不過這個區(qū)域的內(nèi)存收集主要是針對常量池的回收和對類型的卸載。
一般來說方法區(qū)的內(nèi)存回收比較難以令人滿意。當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時將拋出OutOfMemoryError異常。
運(yùn)行時常量池
運(yùn)行時常量池是方法區(qū)的一部分,Class文件中除了有類的版本,字段,方法,接口等信息以外,還有一項信息是常量池用于存儲編譯器生成的各種字面量和符號引用,這部分信息將在類加載后存放到方法區(qū)的運(yùn)行時常量池中。Java虛擬機(jī)對類的每一部分(包括常量池)都有嚴(yán)格的規(guī)定,每個字節(jié)用于存儲哪種數(shù)據(jù)都必須有規(guī)范上的要求,這樣才能夠被虛擬機(jī)認(rèn)可,裝載和執(zhí)行。一般來說,除了保存Class文件中描述的符號引用外,還會把翻譯出來的直接引用也存儲在運(yùn)行時常量池中。
運(yùn)行時常量池相對于Class文件常量池的另外一個重要特征是具備動態(tài)性,Java虛擬機(jī)并不要求常量只能在編譯期產(chǎn)生,也就是并非預(yù)置入Class文件常量池的內(nèi)容才能進(jìn)入方法區(qū)的運(yùn)行時常量池中,運(yùn)行期間也可將新的常量放入常量池中。 常量池是方法區(qū)的一部分,所以受到內(nèi)存的限制,當(dāng)無法申請到足夠內(nèi)存時會拋出OutOfMemoryError異常。
對象訪問
對象訪問在Java語言中無處不在,即使是最簡單的訪問,也會涉及到Java棧,java堆,方法區(qū)這三個最重要的內(nèi)存區(qū)域之間的關(guān)聯(lián)關(guān)系。如下面的代碼:
Object obj = new Object();
假設(shè)這段代碼出現(xiàn)在方法體中,那么“Object obj”部分的語義將會反映到Java棧的本地變量表中,作為一個reference類型的數(shù)據(jù)存在。而“new Object();”部分的語義將會反應(yīng)到Java堆中,形成一塊存儲Object類型所有實例數(shù)據(jù)值(Instance Data)的結(jié)構(gòu)化內(nèi)存,根據(jù)具體類型以及虛擬機(jī)實現(xiàn)的對象分布的不同,這塊內(nèi)存的長度是不固定的。另外,在JAVA堆中還必須包含能查找到此對象內(nèi)存數(shù)據(jù)的地址信息,這些類型數(shù)據(jù)則存儲在方法區(qū)中。
由于reference類型在Java虛擬機(jī)中之規(guī)定了指向?qū)ο蟮囊?,并沒有規(guī)定這個引用要通過哪種方式去定位,以及訪問到Java堆中的對象的具體位置,因此虛擬機(jī)實現(xiàn)的對象訪問方式會有所不同。主流的訪問方式有兩種:句柄訪問方式和直接指針。
如果使用句柄訪問方式,Java堆中將會劃分出一塊內(nèi)存來作為句柄池,reference中存儲的就是對象的地址,而句柄中包含了對象實例數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址信息。
如果通過直接指針方式訪問,Java堆對象的布局中就必須考慮如何放置訪問類型數(shù)據(jù)的相關(guān)信息,reference中直接存儲的就是對象的地址。
兩種方式各有優(yōu)勢,局部訪問方式最大的好處是reference中存放的是穩(wěn)定的句柄地址,在對象被移動時,只會改變句柄中的實例數(shù)據(jù)指針,而reference本身不需要被修改。而指針訪問的最大優(yōu)勢是速度快,它節(jié)省了一次指針定位的開銷,由于對象訪問在Java中非常頻繁,一次這類開銷積少成多后也是一項非??捎^的成本。
具體的訪問方式都是有虛擬機(jī)指定的,虛擬機(jī)Sun HotSpot使用的是直接指針方式,不過從整個軟件開發(fā)的范圍來看,各種語言和框架使用句柄訪問方式的情況十分常見。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64754.html
摘要:運(yùn)行時數(shù)據(jù)區(qū)域之所以要劃分這么多區(qū)域出來是因為這些區(qū)域都有自己的用途,以及創(chuàng)建和銷毀的時間。,運(yùn)行時常量池它是方法區(qū)的一部分。直接內(nèi)存直接內(nèi)存并不是虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)的一部分,也不是虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域。 前言 說到JAVA內(nèi)存區(qū)域,可能很多人第一反應(yīng)是堆棧。首先,堆棧不是一個概念,而是兩個概念,堆和棧是兩塊不同的內(nèi)存區(qū)域,簡單理解的話,堆是用來存放對象而棧是用來運(yùn)行程序的。其次...
摘要:看來還是功力不夠,索性拆成了六篇文章,分別從自動內(nèi)存管理機(jī)制類文件結(jié)構(gòu)類加載機(jī)制字節(jié)碼執(zhí)行引擎程序編譯與代碼優(yōu)化高效并發(fā)六個方面來做更加細(xì)致的介紹。本文先說說虛擬機(jī)的自動內(nèi)存管理機(jī)制。在類加載檢查通過后,虛擬機(jī)將為新生對象分配內(nèi)存。 歡迎關(guān)注微信公眾號:BaronTalk,獲取更多精彩好文! 書籍真的是常讀常新,古人說「書讀百遍其義自見」還是蠻有道理的。周志明老師的這本《深入理解 Ja...
摘要:運(yùn)行時數(shù)據(jù)區(qū)域的學(xué)習(xí),是學(xué)習(xí)以及機(jī)制的基礎(chǔ),也是深入理解對象創(chuàng)建及運(yùn)行過程的前提。了解內(nèi)存區(qū)域劃分,是學(xué)習(xí)概念的前提。 Java 運(yùn)行時數(shù)據(jù)區(qū)域的學(xué)習(xí),是學(xué)習(xí) jvm 以及 GC 機(jī)制的基礎(chǔ),也是深入理解 java 對象創(chuàng)建及運(yùn)行過程的前提。廢話不多說,直接進(jìn)入正題: 一張圖總結(jié) showImg(https://segmentfault.com/img/bVOMAn?w=685&h=5...
摘要:堆區(qū)堆是虛擬機(jī)所管理的內(nèi)存中最大的一塊,它是被所有線程共享的一塊內(nèi)存區(qū)域,該區(qū)域在虛擬機(jī)啟動的時候創(chuàng)建。 運(yùn)行時數(shù)據(jù)區(qū)域 ? ?想要了解jvm,那對其內(nèi)存分配管理的學(xué)習(xí)是必不可少的;java虛擬機(jī)在執(zhí)行java程序的時候會把它所管理的內(nèi)存劃分成若干數(shù)據(jù)區(qū)域。這些區(qū)域有著不同的功能、用途、創(chuàng)建/銷毀時間。java虛擬機(jī)所分配管理的內(nèi)存區(qū)域如圖1所示 程序計數(shù)器 ? ?程序計數(shù)器是一塊比較...
摘要:深入理解虛擬機(jī)高級特性與最佳實踐第二版讀書筆記與常見面試題總結(jié)本節(jié)常見面試題介紹下內(nèi)存區(qū)域運(yùn)行時數(shù)據(jù)區(qū)。運(yùn)行時數(shù)據(jù)區(qū)域虛擬機(jī)在執(zhí)行程序的過程中會把它管理的內(nèi)存劃分成若干個不同的數(shù)據(jù)區(qū)域。 《深入理解Java虛擬機(jī):JVM高級特性與最佳實踐(第二版》讀書筆記與常見面試題總結(jié) 本節(jié)常見面試題: 介紹下Java內(nèi)存區(qū)域(運(yùn)行時數(shù)據(jù)區(qū))。 對象的訪問定位的兩種方式。 1 概述 對于Java...
閱讀 1368·2021-11-24 09:39
閱讀 1358·2021-11-04 16:12
閱讀 2701·2021-09-24 09:47
閱讀 3346·2021-09-01 10:50
閱讀 1487·2019-08-30 15:55
閱讀 1432·2019-08-30 15:43
閱讀 652·2019-08-30 11:08
閱讀 3588·2019-08-23 18:33