一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

2022-01-17 11:43沒頭腦遇到不高興 Java教程

本節(jié)將會介紹一下JVM的內(nèi)存結(jié)構(gòu),JVM運行時數(shù)據(jù)區(qū)的各個組成部分:堆,方法區(qū),程序計數(shù)器,Java虛擬機棧,本地方法棧,還會對Java堆的分代劃分做個簡單的介紹

 

前言

JVM是Java中比較難理解和掌握的一部分,也是面試中被問的比較多的,掌握好JVM底層原理有助于我們在開發(fā)中寫出效率更高的代碼,可以讓我們面對OutOfMemoryError時不再一臉懵逼,可以用掌握的JVM知識去查找分析問題、去進行JVM的調(diào)優(yōu)、去讓我們的應(yīng)用程序可以支持更高的并發(fā)量等。。。。。。總之一句話,學(xué)好JVM很重要!

 

JVM是什么

JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的,注意JVM是基于軟件的,不是基于硬件的。

Java語言的一個非常重要的特點就是與平臺的無關(guān)性。而使用Java虛擬機是實現(xiàn)這一特點的關(guān)鍵。一般的高級語言如果要在不同的平臺上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機后,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用模式Java虛擬機屏蔽了與具體平臺相關(guān)的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行。Java虛擬機在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行。

比如下圖:我們編譯后產(chǎn)生的.class文件是二進制的字節(jié)碼,字節(jié)碼是不能被機器直接運行的,通過JVM把編譯好的字節(jié)碼轉(zhuǎn)換成對應(yīng)操作系統(tǒng)平臺可以直接識別運行的機器碼指令,JVM充當了一個中間轉(zhuǎn)換的橋梁,這樣我們編寫的Java文件就可以做到 "一次編譯,到處運行" 。

深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

 

JVM內(nèi)存結(jié)構(gòu)概覽

JVM虛擬機規(guī)范官方文檔地址:https://docs.oracle.com/javase/specs/,JDK8虛擬機參考手冊地址:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html,JDK8官方文檔地址為https://docs.oracle.com/javase/8/docs/,JDK8中內(nèi)存結(jié)構(gòu)文檔https://docs.oracle.com/javase/specs/jvms/se8/html/index.html。

我們先看下下面這張圖(這張圖非常重要!非常重要!非常重要!),一個Java文件的執(zhí)行過程為:Hello.java文件通過javac被編譯為Hello.class文件,然后類裝載子系統(tǒng)將class文件加載到運行時數(shù)據(jù)區(qū),通過執(zhí)行引擎去執(zhí)行生成的機器指令。

Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,這個數(shù)據(jù)區(qū)域就叫運行時數(shù)據(jù)區(qū)。運行時數(shù)據(jù)區(qū)主要包含了PC寄存器(程序計數(shù)器)、Java虛擬機棧、本地方法棧、Java堆、方法區(qū)以及運行時常量池,這其中Java堆、方法區(qū)跟Java虛擬機棧是學(xué)習(xí)的重點。

但是,需要注意的是,上面的區(qū)域劃分只是邏輯區(qū)域,對于有些區(qū)域的限制是比較松的,所以不同的虛擬機廠商在實現(xiàn)上,甚至是同一款虛擬機的不同版本也是不盡相同的。

深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

 

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

 

程序計數(shù)器

程序計數(shù)器(Program counter Register,也叫PC寄存器)是一塊較小的內(nèi)存空間,是線程私有的,它可以看作是當前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機的概念模型里(僅是概念模到,各種虛擬機可能會通過一些更高效的方式去實現(xiàn)),字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需耍依賴這個計數(shù)器來完成。因為Java是可以多線程執(zhí)行的,一個線程執(zhí)行到一半可能因為CPU時間片輪轉(zhuǎn)切換到了另外一個線程,在切換回之前線程的時候,需要回到線程上次的執(zhí)行位置,所以要線程私有。

如果線程正在執(zhí)行的是一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是Native方法,這個計數(shù)器值則為空(Undefined)。此內(nèi)存區(qū)城是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情祝的區(qū)域。

比如在下面代碼中test1()中調(diào)用了test2(),test2()執(zhí)行完成后退出,這時候需要回到test1()方法中繼續(xù)執(zhí)行,程序計數(shù)器記錄了下一個需要執(zhí)行的指令的行號。

public void test1(){
    test2();
    System.out.println("test1");
}
 
public void test2(){
    System.out.println("test2");
}

 

Java虛擬機棧

Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同到都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。棧幀是Java方法運行時的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬棧中從入棧到出棧的過程(說人話就是要執(zhí)行一個方法,將該方法的棧幀壓入棧頂,方法執(zhí)行完成其棧幀出棧)。在JVM里面,棧幀的操作只有兩種:出棧和入棧。正在被線程執(zhí)行的方法稱為當前線程方法,而該方法的棧幀就稱為當前幀。

局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、long、float、double)、對象引用(reference類型,它不等同于對象本身,可能是一個指向?qū)ο笫嫉刂返囊弥羔槪部赡苁侵赶蛞粋€代表對象的句柄或其地與此對象相關(guān)的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)。

在Java虛擬機規(guī)范中,對這個區(qū)域定了兩種異狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverFlowError異常;一般的虛擬機棧都是可擴展的,如果擴展時無法豐請到足夠的內(nèi)存,就會拋出OutOfMemoryError異常,可以通過-Xss設(shè)置每個線程的堆棧大小。

Java虛擬機棧的結(jié)構(gòu)如下圖所示:Java虛擬機棧的生命周期與線程一致,一個方法對應(yīng)一塊棧幀內(nèi)存區(qū)域,棧幀中包含局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。拿下面代碼舉例,程序執(zhí)行main(),main()先壓入棧頂,然后main()方法中new了一個Math對象,math變量是指向堆中Math對象的引用,math變量就屬于局部變量表,創(chuàng)建Math對象之后,調(diào)用了其compute(),然后compute()壓入棧頂,compute方法執(zhí)行完成后其棧幀出棧,然后根據(jù)程序計數(shù)器記錄程序執(zhí)行的行號,繼續(xù)回到main方法執(zhí)行,main方法中已經(jīng)沒有其他執(zhí)行指令了,則main方法退出,main方法對應(yīng)的棧幀出棧,虛擬機棧中已經(jīng)沒有其他棧幀,main線程生命周期結(jié)束。

注意:關(guān)于Java虛擬機棧中的棧幀,還有棧幀中的組成部分,這里只是做個簡單的概述,后續(xù)會單獨進行詳細講解,希望繼續(xù)關(guān)注。

public class Math {
 
	private static final Integer CONSTANT=666;
	
	private int compute() {//一個方法對應(yīng)一塊棧幀內(nèi)存區(qū)域
		int a=3;
		int b=5;
		int c=(a+b)*10;
		return c;
	}
	
	public static void main(String[] args) {
		Math math=new Math();
		math.compute();
	}
}

深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

 

本地方法棧

本地方法棧(Native Method Stack)與虛擬機棧非常相似,也是線程私有的,它們的區(qū)別不過是虛擬機棧執(zhí)行的是Java方法(也就是字節(jié)碼),而本地方法棧用到的是Native方法。與虛擬機戰(zhàn)一樣。本地方法棧區(qū)域也會出現(xiàn)StackOverFlowError和OutOfMemoryError異常。

 

方法區(qū)

方法區(qū)(Method Area),是各個線程共享的內(nèi)存區(qū)域,,它用于存儲已被虛擬機加載的類型信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼緩存等數(shù)據(jù)。雖然JVM規(guī)范將方法區(qū)描述為堆的一個邏輯部分,但它卻還有一個別名叫做Non一Heap(非堆),目的就是要和堆分開。這部分存儲的是運行時必須的類相關(guān)信息,裝載進此區(qū)域的數(shù)據(jù)是不會被垃圾收集器回收的,只有關(guān)閉Jvm才會釋放這塊區(qū)域占用的內(nèi)存。

對于Hotspot虛擬機,很多開發(fā)者習(xí)慣將方法區(qū)稱之為“永久代(Parmanent Gen)",但嚴格本質(zhì)上說兩者不同,或者說使用永久代來實現(xiàn)方法區(qū)而己,永久代是方法區(qū)(相當于是一個接口interface)的一個實現(xiàn),idkl.7的版本中,己經(jīng)將原本放在永久代的字符串常量池移走。Jdk1.7中方法區(qū)是用永久代實現(xiàn)的,到1.8中是用元空間(MetaSpace)實現(xiàn)的,而元空間使用的是直接內(nèi)存。

根據(jù)Java虛擬機規(guī)范的規(guī)定,當方法區(qū)無法滿足內(nèi)存分配需求時會拋出OutOfMemoryError異常。可以通過-XX:PermSize和 -XX:MaxPermSize來分別設(shè)置永久區(qū)最小、最大空間。

 

運行時常量池

運行時常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯期生產(chǎn)的各種字面量和符號引用,這部分內(nèi)容在類加載后進入方法區(qū)的運行時常量池中存放。

常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近于Java語言層面的常量概念,如文本字符串、聲明為final的常量值等。而符號引用則屬于編譯原理方面的概念,包括了下面三類常量:

  • 類和接口的全限定名(Fully Qualified Name)
  • 字段的名稱和描述符(Descriptor)
  • 方法的名稱和描述符

Java代碼在進行Javac編譯的時候,并不像C和C++那樣有“連接”這一步驟,而是在虛擬機加載Class文件的時候進行動態(tài)連接。也就是說,在Class文件中不會保存各個方法、字段的最終內(nèi)存布局信息,因此這些字段、方法的符號引用不經(jīng)過運行期轉(zhuǎn)換的話無法得到真正的內(nèi)存人口地址,也就無法直接被虛擬機使用。當虛擬機運行時,需要從常量池獲得對應(yīng)的符號引用,再在類創(chuàng)建時或運行時解析、翻譯到具體的內(nèi)存地址之中。

Java語言不要求常量一定只有編譯器才能產(chǎn)生,運行時也可能將新的常量放入池中,該特性用的比較多的就是String類的intern()方法。運行時常量池是方法區(qū)的一部分,在內(nèi)存不夠時,也會拋出OutOfMemoryError異常。

 

Java堆

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

Java堆是被收集管理的主要區(qū)域,因此很多時候也被稱做"GC堆"(Garbage Collected Heap)。從內(nèi)存回收角度來看,由于現(xiàn)在收集器基本都采用分代算法(為什么要采用分代算法,常用的垃圾收集算法有哪些后面會進行介紹),所以堆中還以細分:新生代(Young/New)和老年代(Old/Tenure),新生代又可以劃分為Eden(伊甸園)空間、survivor(幸存區(qū),其又可以分為from survivor和to survivor,也就是S0和S1)空間等。從內(nèi)存分配的角度來看,線程共享的Java堆中可劃分出多個程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)。不過無論如何劃分,都與存放內(nèi)容無關(guān),無論哪個區(qū)域,存儲的都仍然是對象實例,進一步劃分的是為了更好地回收內(nèi)存,或更快地分配內(nèi)存。

根據(jù)Java虛擬機規(guī)范的規(guī)定,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只邏輯上是連續(xù)的即可。.Java虛擬機中可以對堆進行擴展,可以通過-Xms 設(shè)置起始堆大小、通過-Xmx設(shè)置最大堆大小、通過-XX:NewSize設(shè)置新生代最小空間大小、通過 -XX:MaxNewSize設(shè)置新生代最大空間大小。如果在堆中沒有完成實例分配,并且地也無法再擴展時,將會拋OutOfMemoryError異常。

下圖是Java7中的Jvm內(nèi)存劃分:

  • 堆(Heap)、永久代(PermGen)
  • 堆(Heap)又分為新生代(NewGen)或者叫年輕代(YoungGen)、老年代(OldGen)
  • 年輕代(YoungGen)又可分為Eden區(qū)(伊甸園區(qū))、Survivor區(qū)(幸存區(qū))
  • Survivor區(qū)(幸存區(qū))又可分為FromSpace(S0)和ToSpace(S1),整個年輕代中默認比例Eden:S0:S1=8:1:1,同一時間內(nèi)S0跟S1只會有一個區(qū)域被占用
  • 年輕代(New):年輕代用來存放JVM剛分配的Java對象
  • 年老代(Old):年輕代中經(jīng)過垃圾回收沒有回收掉的對象將被Copy到年老代
  • 永久代(Perm):永久代存放Class、Method元信息,其大小跟項目的規(guī)模、類、方法的量有關(guān)
  • 年輕代發(fā)生的GC叫Minor GC,老年代發(fā)生的GC叫Major GC
  • 另外還有一個Full GC,是清理整個堆空間―包括年輕代和永久代

關(guān)于堆的分代、還有對象是如何從年輕代進入老年代等都會在后面的章節(jié)中介紹。

深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

我們看下面這張圖,在JDK1.8中將永久代去掉了,改由元空間(MetaSpace)去實現(xiàn)方法區(qū),而元空間跟永久代的最大區(qū)別就是其不在JVM內(nèi)存中,而是使用的直接內(nèi)存。

深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)

關(guān)于方法區(qū)、常量池、永久代在JDK6、7、8中的變動還是挺大的:

  • Jdk1.6及之前:有永久代,常量池1.6在方法區(qū)
  • Jdk1.7:有永久代,但己經(jīng)逐步“去永久代”,常量池1.7在堆
  • Jdk1.8及之后:無永久代,常量池1.8在元空間

 

直接內(nèi)存

直接內(nèi)存(Direct Memory)并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域。在JDK1.4中新加入了NlO(New Inpu/Output)類,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)。

顯然,本機直接內(nèi)存的分配不會受到Java堆大小的限制,但是,既然是內(nèi)存,肯定還是會受到本機總內(nèi)存(包括RAM以及SWAP區(qū)或者分頁文件)大小以及處理器尋址空間的限制。當各個內(nèi)存區(qū)域總和大于物理內(nèi)存限制(包括物理的和操作系統(tǒng)級的限制),會導(dǎo)致動態(tài)擴展時出現(xiàn)OutOfMemoryError異常。

關(guān)于JVM的內(nèi)存結(jié)構(gòu)本節(jié)先做了一個大概的介紹,其中還有很多細節(jié)沒有介紹:棧幀中的各個組成部分分別是干什么用的,堆內(nèi)存的劃分,對象是如果從新生代到老年代的,為什么要分代收集,垃圾收集算法有哪些,垃圾收集器有哪些。。。。。這些在后面的章節(jié)中會慢慢一一介紹,希望繼續(xù)關(guān)注。

文章內(nèi)容參考了周志明老師的《深入理解Java虛擬機第二版》以及他翻譯的《Java虛擬機規(guī)范 JavaSE8版》,想學(xué)習(xí)JVM的話強烈推薦這本《深入理解Java虛擬機第二版》。

到此這篇關(guān)于深入理解Java虛擬機 JVM 內(nèi)存結(jié)構(gòu)的文章就介紹到這了,更多相關(guān)Java 虛擬機 JVM 內(nèi)存結(jié)構(gòu)內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/u012988901/article/details/99740694

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實現(xiàn)搶紅包功能

    Java實現(xiàn)搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 亚洲国产成人久久77 | 五月最新商场女厕所高跟嘘嘘 | 嫩草在线观看视频 | melody中文字幕 | 秋葵视频成人 | 免费视频片在线观看 | 久久精品视频uu | 久久精品中文騷妇女内射 | 日本人妖在线 | 国内精品一区二区三区东京 | 亚洲丰满女人ass硕大 | 国产免费福利片 | 日韩成人影视 | 欧美区一区 | 91短视频社区在线观看 | 国产精品久久国产三级国电话系列 | 国产在线观看福利片 | 爱色成人网 | 韩剧消失的眼角膜免费完整版 | 青青在线视频免费 | 日韩亚洲人成在线综合 | 校花被拖到野外伦小说 | 教师系列 大桥未久在线 | ccc在线在线36| 国产欧美日韩在线不卡第一页 | 无限在线看免费视频大全 | 99视频在线看 | 性欧美13处丶14处 | 色综合综合 | 日本美女视频韩国视频网站免费 | ass性强迫rape | 精品在线99 | 免费观看视频在线播放 | 91精品大神国产在线播放 | 亚洲第一色区 | 精品欧美小视频在线观看 | 性做久久久久久久久老女人 | 暖暖的免费观看高清视频韩国 | 国产一区在线 | 四虎影院永久网址 | 亚洲精品丝袜在线一区波多野结衣 |