您好,登錄后才能下訂單哦!
前言:
我們每天都在編寫Java代碼,編譯,執行。很多人已經知道Java源代碼文件(.java后綴)會被Java編譯器編譯為字節碼文件(.class后綴),然后由JVM中的類加載器加載各個類的字節碼文件,加載完畢之后,交由JVM執行引擎執行。
那在整個程序執行過程中,JVM中怎么存取數據和相關信息呢?
事實上在JVM中是用一段空間來存儲程序執行期間需要用到的數據和相關信息,這段空間一般被稱作為Runtime Data Area(運行時數據區),也就是我們常說的JVM內存。
一、運行時數據區域包括哪些?
根據《Java虛擬機規范》的規定,運行時數據區通常包括這幾個部分:程序計數器(Program Counter Register)、Java虛擬機棧(Java Vitual Machine Stack)、本地方法棧(Native Method Stack)、方法區(Method Area)、堆(Heap)。
二、各個部分存儲的信息和負責的職能
1、程序計數器
這個內存區域是Java虛擬機規范中唯一一個沒有規定任何OOM(OutOfMemoryError)情況的區域,這是這個區域最大的特點之一,這是因為程序計數器中存儲的數據所占空間的大小不會隨程序的執行而發生改變,因此,對于程序計數器是不會發生內存溢出現象(OutOfMemory)的。
這個區域主要是負責記錄正在執行的虛擬機字節碼指令地址,即當前線程執行的字節碼的行號指示器(注意:JVM不是直接執行Java代碼,而是執行.class文件,所以只要其他編程語言能翻譯成.class文件一樣能放入JVM中執行)。
JVM會給每個線程一個獨立的程序計數器,計數器之間互不影響,且通過線程輪流切換并且分配處理器執行時間來實現JVM的多線程。不過當線程執行的是Native方法的時候這個計數器中的值為undefined。
2、Java虛擬機棧
和程序計數器一樣的是Java虛擬機棧是線程私有,生命周期和線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的時候都會創建棧幀,用來存儲局部變量表,操作數棧,動態鏈接,方法出口等信息,每個方法從調用到執行完成的過程,就對應一個棧幀在虛擬機中入棧到出棧的過程,其中64位長度的long和double類型的數據會占用2個局部變量空間,其余的數據類型只占用1個。
這里需要理解一下的就是為什么要用棧這個結構呢,比如A方法中調用了B方法,虛擬機中是先讓A方法的棧幀進入虛擬機棧執行,當執行到調用B方法的語句就讓B棧幀進入,執行完之后B棧幀就出棧,A棧就繼續執行。這里注意的是如果遞歸的方法遞歸的太深很容易拋出下面兩種異常,所以遞歸雖然寫起來方便,但是性能會有所下降,并且容易拋出異常。
Java虛擬機規范中,對這個區域規定了兩種異常狀況
3、本地方法棧
與虛擬機棧作用相似,不過是虛擬機棧為虛擬機執行Java方法提供,而本地方法為虛擬機使用到的Native方法服務,Native方法多是用C++寫的。拋出的異常和虛擬機棧相同。
4、Java堆
Java堆是與前面的區域不同的是:這個區域是被所有線程共享的一塊內存區域,用來存放對象實例,并為對象實例分配好內存。Java虛擬機規范中這樣描述:所有對象實例以及數組都要在堆上分配Java堆也是垃圾收集器管理的主要區域,也叫”GC堆“。由于現在的垃圾回收算法多是分代收集,所以Java堆里面又可分為:新生代和老年代。并且根據Java虛擬機規范的規定:Java堆可以處于物理上不連續的內存空間中,只要邏輯上連續即可。有實例沒有被分配,且堆無法再擴展的時候會拋出OutOfMemoryError異常,虛擬機調優其實也主要關注的是這個區域。
5、方法區
與Java堆一樣,線程共享,用來存儲被虛擬機加載的類信息,常量,靜態變量。這個區域Java虛擬機規范對其特別寬松,既可以像Java堆那樣不需要連續內存,又可以選擇固定大小和可擴展。還可以選擇不實現垃圾收集,這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載。當無法滿足內存分配需求時,將拋出OutOfMemoryError異常。
目前虛擬機Hotspot已經將這部分存儲空間從使用JVM內存換成使用本地內存,即這部分不再叫永久代,而是元空間。這個元空間實際上是JVM動態規定內存大小。這個替換有什么優勢呢?因為字符串常量池是存在永久代中,很容易出現性能問題,并且類和方法信息大小難確定,給永久代的的大小指定帶來困難,而且GC會對永久代特殊處理,這就增加了GC的復雜性。從JDK1.7開始,字符串常量池就劃分進了堆中,其他的更多是元空間在內存劃分的算法上更趨于合理
6、運行時常量池
是方法區的一部分。用于存放編譯期生成的各種字面量和符號引用,同時也會把翻譯出來的直接引用也存儲在運行時的常量池中,具有動態性。常量不一定只有編譯期才能產生,運行期間也可以將新的常量放入池中。例如String的Intern()方法。同樣拋出OutOfMemoryError異常
三、直接內存
這個區域并不是屬于運行時數據區域,但是這個區域也會被頻繁使用,并且拋出OOM異常。這個區域主要是由于在JDK1.4中新加入了NIO(New Input/Output)類,引入了一種基于通道與緩沖區的I/O方式,它可以使用Native函數庫直接分配堆外內存,通過一個儲存在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。
這樣能避免在Java堆和Native堆中來回復制數據,從而在一些場景中顯著提高性能。直接內存分配不會受到Java堆大小的限制,會受到本機總內存大小及處理器尋址空間的限制。會拋出OutOfMemoryError異常
四、總結
只有程序計數器不會報出任何相關OOM異常,而Java虛擬機棧有可能會報出OOM或Stack Overflow異常。Java虛擬機棧主要是存儲方法的一些信息,能讓方法順利的執行,而Java堆存儲的是對象的信息。虛擬機的垃圾回收算法主要在這一塊,并且平常調優的區域也是在這一塊。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。