您好,登錄后才能下訂單哦!
本篇內容主要講解“Java虛擬機的內存結構是怎樣的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java虛擬機的內存結構是怎樣的”吧!
一:簡介
內存(Memory)也被稱為內存儲器,其作用是用于暫時存放CPU中的運算數據,以及與硬盤等外部存儲器交換的數據。只要計算機在運行中,CPU就會把需要運算的數據調到內存中進行運算,當運算完成后CPU再將結果傳送出來。
Java虛擬機在執行Java程序過程中會把它所管理的內存劃分為若干個不同的數據區域。這些區域都有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的啟動而存在,有些區域則依賴用戶線程的啟動和結束而建立和銷毀。
進程:一段程序的執行過程,是一個具有一定獨立功能的程序關于某個數據集合的一次運行活動。它是操作系統動態執行的基本單元,在傳統的操作系統中,進程既是基本的分配單元,也是基本的執行單元。
進程是一個實體。每一個進程都有它自己的地址空間,一般情況下,包括文本區域(text region)、數據區域(data region)和堆棧(stack region)。文本區域存儲處理器執行的代碼;數據區域存儲變量和進程執行期間使用的動態分配的內存;堆棧區域存儲著活動過程調用的指令和本地變量。第二,進程是一個“執行中的程序”。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成為一個活動的實體,我們稱其為進程。
進程有三個狀態,就緒、運行和阻塞。就緒狀態其實就是獲取了出cpu外的所有資源,只要處理器分配資源就可以馬上執行。就緒狀態有排隊序列什么的,排隊原則不再贅述。運行態就是獲得了處理器分配的資源,程序開始執行。阻塞態,當程序條件不夠時候,需要等待條件滿足時候才能執行,如等待i/o操作時候,此刻的狀態就叫阻塞態。
線程:通常在一個進程中可以包含若干個線程,當然一個進程中至少有一個線程,不然沒有存在的意義。線程可以利用進程所擁有的資源,在引入線程的操作系統中,通常都是把進程作為分配資源的基本單位,而把線程作為獨立運行和獨立調度的基本單位,由于線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提高系統多個程序間并發執行的程度。
進程和線程的主要差別:在于它們是不同的操作系統資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些
二:程序計數器
程序計數器是一塊較小的內存空間,它可以看作是當前線程執行的字節碼行號的指示器。
字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令;分支,循環,跳轉,異常處理,線程恢復等基礎功能都需要依賴這個計數器來完成
當線程獲得時間片處于執行過程時,CPU會按照程序計數器中存儲的內容依次的取出指令執行,當CPU取出當前指令時,CPU自動修改程序計數器內容,使其指向下一條指令字節碼行號。由于程序計數器中存儲的是數字值,因此不會隨著程序運行而擴大需求空間,故不會發生溢出。
假設線程A正在執行,當執行到某個階段,優先級更高線程B執行。此時,線程A掛起,線程B執行。當線程B執行完畢后,需要喚醒線程A繼續執行,那么如何從線程A的中斷位置繼續執行呢,那就需要CPU訪問線程A的程序計數器,從中獲取下一條執行指令,保證程序繼續執行。由于每個線程需要保存自身的執行位置,也就使得程序計數器為線程私有。
native本地方法大多是通過C實現并未編譯成需要執行的字節碼指令,所以在計數器中是undefined
這個內存區域是唯一一個在java虛擬界規范中沒有規定任何OutOfMemoryError的情況的區域。
三:Java虛擬機棧
它是一個后入先出的棧,其中保存的元素是棧幀 。它也是線程私有的,它的生命周期與線程相同。
程序運行時,每調用一個方法就會生成一個棧幀,同時將當前正在執行方法的棧幀壓入虛擬機棧。虛擬機棧頂的棧幀為“當前活躍棧幀”。既然棧幀是調用方法時創建,那么其保存內容必然與方法息息相關。
局部變量表:局部變量表是一組局部變量值存儲空間,用于存放方法參數和方法內部定義的局部變量。局部變量表存放了編譯器可知的各種基本數據類型,對象引用和returnAddress類型。局部變量表的內存是在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
操作數棧:操作數棧是一個以字長為單位的數組。但它不是通過索引來訪問,而是通過標準的棧操作—壓棧和出棧—來訪問的。比如,如果某個指令A把一個值1壓入到操作數棧中,指令B將值2壓入棧中,稍后指令C就可以彈出這兩個個值來進行相加計算,并將結果3壓入棧中。通過操作數棧可以完成方法中的一些運算。
動態鏈接:每個棧幀內部都包含一個指向當前方法所在類型的運行時常量池的引用,以便對當前方法的代碼實現動態鏈接。在class文件里面,一個方法如果要調用另外一個方法,或者訪問其成員變量,則需要通過符號引用來表示,那么動態鏈接的作用就是在恰當的時候將這些以符號引用所表示的方法或是變量解析成直接引用。
返回地址:一般來說,方法正常退出時,調用者PC計數器的值就可以作為返回地址,棧幀中很可能會保存這個計數器值。而方法異常退出時,返回地址是要通過異常處理器來確定的,棧幀中一般不會保存這部分信息。 方法退出的過程實際上等同于把當前棧幀出棧,因此退出時可能執行的操作有:恢復上層方法的局部變量表和操作數棧,把返回值(如果有的話)壓入調用棧幀的操作數棧中,調用PC計數器的值以指向方法調用指令后面的一條指令等。
其他信息:虛擬機規范允許具體的虛擬機實現增加一些規范里沒有描述的信息到棧幀中,例如與高度相關的信息,這部分信息完全取決于具體的虛擬機實現。
3.如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常; 如果虛擬機棧可以動態擴展,如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
四:本地方法棧
本地方法棧與虛擬機棧所發揮的作用是非常相似的,它們之間的區別不過是虛擬機棧是為虛擬機執行Java方法服務,而本地方法棧則為虛擬機使用到的Native方法服務。
本地方法棧區域也會拋出StackOverflowError和OutOfMemoryError異常。
五:Java堆
Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。
Java堆一般分為三大部分:新生代,老年代和永久代
新生代:主要是用來存放新生的對象。一般占據堆的1/3空間。由于頻繁創建對象,所以新生代會頻繁觸發MinorGC進行垃圾回收。
老年代:主要存放應用程序中生命周期長的內存對象。老年代的對象比較穩定,所以MajorGC不會頻繁執行。在進行MajorGC前一般都先進行了一次MinorGC,使得有新生代的對象晉身入老年代,導致空間不夠用時才觸發。當無法找到足夠大的連續空間分配給新創建的較大對象時也會提前觸發一次MajorGC進行垃圾回收騰出空間。
永久代:指內存的永久保存區域,主要存放Class和Meta(元數據)的信息,Class在被加載的時候被放入永久區域. 它和和存放實例的區域不同,GC不會在主程序運行期對永久區域進行清理。所以這也導致了永久代的區域會隨著加載的Class的增多而脹滿,最終拋出OOM異常。
Java堆是垃圾收集器管理的主要區域。
Java堆可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可。
可以通過-Xmx和-Xms來擴展,如果無法再擴展時,將會拋出OutOfMemoryError異常
六:方法區
方法區與Java堆一樣,是各個線程共享的內存區域,它用于存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯后的代碼等數據。
當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常
到此,相信大家對“Java虛擬機的內存結構是怎樣的”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。