JVM(java虛擬機)過程詳解

原文:https://blog.csdn.net/csdnliuxin123524/article/details/81303711 

1.java自動管理堆(heap)和(棧),程序員不能直接的設置堆和棧。

2.jvm的內存是分佈在操作系統的堆

堆(操作系統):一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收,分配方式類似於鏈表。

棧(操作系統):由操作系統自動分配釋放,存放函數的參數值,局部變量值等。操作方式與數據結構中的棧相類似。


程序員寫好的類加載到虛擬機執行的過程是:當一個classLoder啓動的時候,classLoader的生存地點在jvm中的堆,然後它會去主機硬盤上將A.class裝載到jvm的方法區,方法區中的這個字節文件會被虛擬機拿來new A字節碼(),然後在堆內存生成了一個A字節碼的對象,然後A字節碼這個內存文件有兩個引用一個指向A的class對象,一個指向加載自己的classLoader

3.java的虛擬機種有兩種線程,一種叫叫守護線程,一種叫非守護線程(也叫普通線程),main函數就是個非守護線程,虛擬機的gc(垃圾回收機制)就是一個守護線程。java的虛擬機中,只要有任何非守護線程還沒有結束,java虛擬機的實例都不會退出,所以即使main函數這個非守護線程退出,但是由於在main函數中啓動的匿名線程也是非守護線程,它還沒有結束,所以jvm沒辦法退出

4. java虛擬機的生命週期:聲明週期起點是當一個java應用main函數啓動時虛擬機也同時被啓動,而只有當在虛擬機實例中的所有非守護進程都結束時,java虛擬機實例才結束生命。

java虛擬機與main方法的關係:main函數就是一個java應用的入口,main函數被執行時,java虛擬機就啓動了。啓動了幾個main函數就啓動了幾個java應用,同時也啓動了幾個java的虛擬機。

5.GC垃圾回收機制不是創建的變量爲空是就被立刻回收,而是超出變量的作用域後就被自動回收。

6.首先,當一個程序啓動之前,ClassLoader首先會被加載到jvm內存的堆中,然後後綴爲class的文件會被ClassLoader裝入方法區,執行引擎讀取方法區的字節碼自適應解析,邊解析就邊運行(其中一種方式),然後pc寄存器在移動的過程中在某個時刻會指向main函數所在位置,虛擬機開始爲main函數在java棧中預留一個棧幀(每個方法都對應一個棧幀),然後開始跑main函數,main函數裏的代碼被執行引擎映射成本地操作系統裏相應的實現,然後調用本地方法接口,本地方法運行的時候,操縱系統會爲本地方法分配本地方法棧,用來儲存一些臨時變量,然後運行本地方法,調用操作系統API等等。

7.虛擬機棧、本地方法棧、程序計數器這三個模塊是線程私有的,有多少線程就有多少個這三個模塊,聲明週期跟所屬線程的聲明週期一致。以程序計數器爲例,因爲多線程是通過線程輪流切換和分配執行時間來實現,所以當線程切回到正確執行位置,每個線程都有獨立的程序技術器,各個線程之間的計數器互不影響,獨立存儲。其餘是跟JVM虛擬機的生命週期一致。

8.雙親委派機制(來請求先給父類執行,成功則返回,不成功自己再去加載):JVM在加載類時默認採用的是雙親委派機制。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。


例如:當jvm要加載Test.class的時候,

  (1)首先會到自定義加載器中查找(其實是看運行時數據區的方法區有沒有加載),看是否已經加載過,如果已經加載過,則返回字節碼。

  (2)如果自定義加載器沒有加載過,則詢問上一層加載器(即AppClassLoader)是否已經加載過Test.class。

  (3)如果沒有加載過,則詢問上一層加載器(ExtClassLoader)是否已經加載過。

  (4)如果沒有加載過,則繼續詢問上一層加載(BoopStrap ClassLoader)是否已經加載過。

  (5)如果BoopStrap ClassLoader依然沒有加載過,則到自己指定類加載路徑下("sun.boot.class.path")查看是否有Test.class字節碼,有則返回,沒有通知下一層加載器ExtClassLoader到自己指定的類加載路徑下(java.ext.dirs)查看。

  (6)依次類推,最後到自定義類加載器指定的路徑還沒有找到Test.class字節碼,則拋出異常ClassNotFoundException。

9.程序計數器(Program Counter Register):也叫PC寄存器,是一塊較小的內存空間,它可以看做是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令、分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。,(1),區別於計算機硬件的pc寄存器,兩者不略有不同。計算機用pc寄存器來存放“僞指令”或地址,而相對於虛擬機,pc寄存器它表現爲一塊內存(一個字長,虛擬機要求字長最小爲32位),虛擬機的pc寄存器的功能也是存放僞指令,更確切的說存放的是將要執行指令的地址。(2)當虛擬機正在執行的方法是一個本地(native)方法的時候,jvm的pc寄存器存儲的值是undefined。(3)程序計數器是線程私有的,它的生命週期與線程相同,每個線程都有一個。(4)此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。

Java虛擬機棧(Java Virtual Machine Stack):(1)線程私有的,它的生命週期與線程相同,每個線程都有一個。(2)每個線程創建的同時會創建一個JVM棧,JVM棧中每個棧幀存放的爲當前線程中局部基本類型的變量(java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double;和reference (32 位以內的數據類型,具體根據JVM位數(64爲還是32位)有關,因爲一個solt(槽)佔用32位的內存空間 )、部分的返回結果,非基本類型的對象在JVM棧上僅存放一個指向堆上的地址;(3)每一個方法從被調用直至執行完成的過程就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。(5)棧運行原理:棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個內存區塊,是一個數據集,是一個有關方法和運行期數據的數據集,當一個方法A被調用時就產生了一個棧幀F1,並被壓入到棧中,A方法又調用了B方法,於是產生棧幀F2也被壓入棧,B方法又調用了C方法,於是產生棧幀F3也被壓入棧…… 依次執行完畢後,先彈出後進......F3棧幀,再彈出F2棧幀,再彈出F1棧幀。(6)JAVA虛擬機棧的最小單位可以理解爲一個個棧幀,一個方法對應一個棧幀,一個棧幀可以執行很多指令

(7)對上圖中的動態鏈接解釋下,比如當出現main方法需要調用method1()方法的時候,操作指令就會觸動這個動態鏈接就會找打方法區中對於的method1(),然後把method1()方法壓入虛擬機棧中,執行method1棧幀的指令;此外如果指令表示的代碼是個常量,這也是個動態鏈接,也會到方法區中的運行時常量池找到類加載時就專門存放變量的運行時常量池的數據。

本地方法棧(Native Method Stack):(1)先解釋什麼是本地方法:jvm中的本地方法是指方法的修飾符是帶有native的但是方法體不是用java代碼寫的一類方法,這類方法存在的意義當然是填補java代碼不方便實現的缺陷而提出的。案例介紹將在 下面22知識點仔細介紹。(2)作用同java虛擬機棧類似,區別是:虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。(3)是線程私有的,它的生命週期與線程相同,每個線程都有一個。

Java 堆(Java Heap):(1)是Java虛擬機所管理的內存中最大的一塊。(2)不同於上面3個,堆是jvm所有線程共享的。(3)在虛擬機啓動的時候創建。(4)唯一目的就是存放對象實例,幾乎所有的對象實例以及數組都要在這裏分配內存。(5)Java堆是垃圾收集器管理的主要區域。(6)因此很多時候java堆也被稱爲“GC堆”(Garbage Collected Heap)。從內存回收的角度來看,由於現在收集器基本都採用分代收集算法,所以Java堆還可以細分爲:新生代和老年代;新生代又可以分爲:Eden 空間、From Survivor空間、To Survivor空間。(23知識點詳細介紹)(7)java堆是計算機物理存儲上不連續的、邏輯上是連續的,也是大小可調節的(通過-Xms和-Xmx控制)。(8)如果在堆中沒有內存完成實例的分配,並且堆也無法再擴展時,將會拋出OutOfMemoryError異常。

方法區(Method Area):(1)在虛擬機啓動的時候創建。(2)所有jvm線程共享。(3)除了和堆一樣不需要不連續的內存空間和可以固定大小或者可擴展外,還可以選擇不實現垃圾收集。(5)用於存放已被虛擬機加載的類信息、常量、靜態變量、以及編譯後的方法實現的二進制形式的機器指令集等數據。(4)被裝載的class的信息存儲在Methodarea的內存中。當虛擬機裝載某個類型時,它使用類裝載器定位相應的class文件,然後讀入這個class文件內容並把它傳輸到虛擬機中。(6)運行時常量池(Runtime Constant Pool)是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。

10.java被編譯成了class文件,JVM怎麼從硬盤上找到這個文件並裝載到JVM裏呢?

是通過java本地接口(JNI),找到class文件後並裝載進JVM,然後找到main方法,最後執行。

11.volatile和sychronized的區別

volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取

synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。

volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的

volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性

volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。

volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章