溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

java虛擬機中內(nèi)存管理機制的示例分析

發(fā)布時間:2021-08-21 14:20:48 來源:億速云 閱讀:130 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關(guān)java虛擬機中內(nèi)存管理機制的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

先讓我們來看張圖

java虛擬機中內(nèi)存管理機制的示例分析

運行時數(shù)據(jù)區(qū)

Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,這些區(qū)域都擁有自己的用途,并隨著JVM進程的啟動或者用戶線程的啟動和結(jié)束建立和銷毀。

先讓我們了解下進程和線程的區(qū)別:

進程是資源分配的最小單位,線程是程序執(zhí)行的最小單位。

進程有自己的獨立地址空間,每啟動一個進程,系統(tǒng)就會為它分配一個地址空間、建立數(shù)據(jù)表來維護代碼段、堆棧段和數(shù)據(jù)段,這種操作非常昂貴。而線程是共享進程中的數(shù)據(jù)的,使用相同的地址空間,因此CPU切換一個線程的花費要比進程小很多,同時創(chuàng)建一個線程的開銷也要比進程小很多。

同一個進程中可以包括多個線程,并且線程共享整個進程的資源(寄存器、堆棧、上下文),一個進程至少包含一個線程。線程之間的通信更加方便,同一進程下的線程共享全局變量、靜態(tài)變量等數(shù)據(jù),而進程之間的通信則需要以通信的方式(IPC)進行。

這里引用在知乎中某位同學(xué)的解釋,

進程的顆粒度太大,每次都要有上下的調(diào)入,保存,調(diào)出。如果我們把進程比喻為一個運行在電腦上的軟件,那么一個軟件的執(zhí)行不可能是一條邏輯執(zhí)行的,必定有多個分支和多個程序段,就好比要實現(xiàn)程序A,實際分成 a,b,c等多個塊組合而成。那么這里具體的執(zhí)行就可能變成:

程序A得到CPU =》CPU加載上下文,開始執(zhí)行程序A的a小段,然后執(zhí)行A的b小段,然后再執(zhí)行A的c小段,最后CPU保存A的上下文。

這里a,b,c的執(zhí)行是共享了A的上下文,CPU在執(zhí)行的時候沒有進行上下文切換的。

看到這里是不是對線程共享和線程隔離區(qū)有了一個更深次的理解??梢岳斫鉃榉椒▍^(qū)和堆是分配給進程的,也就是線程共享區(qū),而棧和程序計數(shù)器則是分配給每個獨立線程的。

在SUN公司的HotSpot虛擬機中將java虛擬機棧和本地方法棧合二為一了

程序計數(shù)器(Program Counter Register)

程序計數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它可以看成是當(dāng)前線程所執(zhí)行字節(jié)碼的行號指示器。在計算機中,其實程序計數(shù)器就是一個寄存器,依據(jù)不同計算機細節(jié)的差異,它可以存放當(dāng)前正在被執(zhí)行的指令,也可以放下一個被執(zhí)行的指令。

在虛擬機的概念模型中,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令的。

由于Java虛擬機的多線程是通過線程輪詢切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時候,一個處理器都只會執(zhí)行一條線程中的指令,因此為了線程切換之后能過恢復(fù)到正確的執(zhí)行位置,每條線程都需要擁有一個獨立的程序計數(shù)器,各條線程之間的計數(shù)器互不影響,獨立存儲,所以程序計數(shù)器是線程私有的內(nèi)存,也就是它屬于線程隔離區(qū)的。

如果線程執(zhí)行的是一個Java方法,這個計數(shù)器記錄的就是正在執(zhí)行的虛擬機字節(jié)碼指令地址;如果正在執(zhí)行的是Native方法,那么這個計數(shù)器的值就是(Undefined)。

此內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

Java虛擬機棧

Java虛擬機棧(Java Virtual Machine Stack)也是線程私有的,即他的生命周期和線程相同。

在Java中,JVM中的棧記錄了線程的方法調(diào)用,每個線程擁有一個棧,在某個線程的運行過程中,如果有新的方法調(diào)用,那么該線程對應(yīng)的棧就會增加一個存儲單元,即棧針(Stack Frame)。

虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行時都會創(chuàng)建一個棧針(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等信息。每一個方法從調(diào)用至完成的過程,就對應(yīng)著一個棧幀在虛擬機棧中從入棧到出棧的過程。

當(dāng)被調(diào)用方法運行結(jié)束時,該方法對應(yīng)的幀將被刪除,參數(shù)和局部變量所占據(jù)的空間也隨之釋放。線程回到原方法,繼續(xù)執(zhí)行。當(dāng)所有的棧都清空時,程序也隨之運行結(jié)束。

我們經(jīng)常說的棧內(nèi)存其實就是現(xiàn)在講的虛擬機棧,或者說是虛擬機棧中局部變量表部分。

局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同于對象本身,可能是指向?qū)ο笃鹗嘉恢玫囊弥羔?,也可能是指向一個代表對象的句柄或其他與此對象相關(guān)的位置,引用所指向的對象保存在堆中(引用可能為Null,即不指向任何對象))和returnAddress類型(指向了一條字節(jié)碼指令的地址)。

其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量空間(Slot),其余數(shù)據(jù)類型只占用1個。局部變量表所需要的內(nèi)存空間在編譯時期完成分配。當(dāng)進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

異常有兩種

1,線程請求的棧深度大于虛擬機所允許的深度將拋出StackOverflowError異常 (遞歸調(diào)用)

2,如果虛擬機可以動態(tài)擴展,如果擴展時已經(jīng)無法申請到足夠的內(nèi)存就會拋出OutOfMemeoryError異常。

List list=new ArrayList();
  for(;;){
   int[] tmp=new int[1000000];
   list.add(tmp);
  }

本地方法棧

本地方法棧(Native Method Stack)與虛擬機棧所發(fā)揮的作用是非常相似的。他們之間的區(qū)別就是Java虛擬機棧是位虛擬機執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧為位虛擬機使用到的Native方法服務(wù)。

其實虛擬機規(guī)范中對本地方發(fā)棧中方法所使用的語言、使用方式以及數(shù)據(jù)結(jié)構(gòu)都沒有強制規(guī)定,因此具體的虛擬機可以自由地實現(xiàn)它。甚至在有的虛擬機(如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二為一。與虛擬機棧一樣,本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemory異常。

Java堆

對于大多數(shù)應(yīng)用來說,Java堆(Java Heap)是Java虛擬機管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊數(shù)據(jù)區(qū)域,在虛擬機啟動時創(chuàng)建,這一內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存。但是隨著JIT編譯器的發(fā)展與逃逸分析技術(shù)逐漸成熟,棧上分配、標(biāo)量替換優(yōu)化技術(shù)將會導(dǎo)致一些微妙的變化發(fā)生,所有的對象都分配在堆上也逐漸變得不是那么“絕對”。

堆中可細分為新生代和老年代,在細分可以分為Eden空間、Form Survivor空間、to Survivor空間。

Java堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱為“GC堆”。

根據(jù)Java虛擬機規(guī)范規(guī)定,Java堆可以處于物理上不連續(xù)的內(nèi)存中,即只要邏輯上是連續(xù)的即可,就像我們的磁盤空間一樣。在實現(xiàn)時,可以固定大小也是可擴展的。主流的虛擬機都是按照可擴展來實現(xiàn)的(通過-Xmx和-Xms來控制)。如果在堆中沒有內(nèi)存可分配,并且堆也無法繼續(xù)擴展時,將會拋出OutOfMemortError異常。

Java的普通對象存活在堆中,與棧不同,堆的空間不會隨著方法調(diào)用結(jié)束而清空。因此,在某個方法中創(chuàng)建的對象,可以在方法調(diào)用結(jié)束之后,繼續(xù)存在堆中。這帶來的一個問題是,如果我們不斷的創(chuàng)建新的對象,內(nèi)存控件將會最終消耗殆盡。

方法區(qū)

方法區(qū)(Method Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已經(jīng)被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯之后的代碼等數(shù)據(jù)。雖然Java虛擬機將其描述為堆的一個邏輯部分,但它卻有一個別名叫做Non-Heap(非堆)。目的是與Java堆區(qū)分開來。(以前很多人把方法區(qū)稱為永久代,現(xiàn)在JDK1.8中已經(jīng)用元數(shù)據(jù)區(qū)域取代了永久代)。

運行時常量池

運行時常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池。用于存放編譯期生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進入到方法區(qū)的運行時常量池中存放。并非預(yù)置入Class文件中常量池的內(nèi)容才進入方法運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用得比較多的便是String類的intern()方法。

public class Test
{
 public static void main(String[] args)
 {
  String s1="hello china";//字節(jié)碼常量
  String s2="hello china";
  String s3=new String("hello china");

  System.out.println(s1 == s2);
  System.out.println(s1 == s3);
  System.out.println(s1 == s3.intern());//運行時常量 intern 是個native方法
 }
}

java虛擬機中內(nèi)存管理機制的示例分析

當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,拋出OutOfMemoryError

注:JDK8之前,方法區(qū)由永久代實現(xiàn),主要存放類的信息、常量池、方法數(shù)據(jù)、方法代碼等;JDK8之后,取消了永久代,提出了元空間,并且常量池、靜態(tài)成員變量等遷移到了堆中;元空間不在虛擬機內(nèi)存中,而是放在本地內(nèi)存中。

直接內(nèi)存

由于直接內(nèi)存(Direct Memory)并不是java虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域,但是這部分也被頻繁使用,而且也可能導(dǎo)致內(nèi)存溢出異常出現(xiàn),所以也放到這一部分進行簡介。

先讓,本機直接內(nèi)存的分配不會受到Java堆大小的限制,但是肯定還是會受到本機總內(nèi)存大小以及處理器尋址空間的限制。管理員在配置虛擬機參數(shù)時,會根據(jù)實際內(nèi)存設(shè)置-Xmx等參數(shù)信息,但經(jīng)常會忽略直接內(nèi)存,使得各個內(nèi)存區(qū)域總和大于物理內(nèi)存限制(包含物理的和操作系統(tǒng)級的限制),從而導(dǎo)致動態(tài)擴展時出現(xiàn)OutOfMemoryError異常。

JDK1.4加入了NIO,引入一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。因此避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù),提高了性能。 

Object obj = new Object();

Object obj將會反映到虛擬機棧中(reference類型)

new Object()將會反映到Java堆中

此類的對象類型、父類、實現(xiàn)的接口、方法等信息數(shù)據(jù),將反映到方法區(qū)中

感謝各位的閱讀!關(guān)于“java虛擬機中內(nèi)存管理機制的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI