Java虛擬機器詳解02----JVM記憶體結構

來源:互聯網
上載者:User

標籤:編譯   store   壓力   a20   標識   java程式   技術   create   package   

主要內容如下:

  • JVM啟動流程
  • JVM基本結構
  • 記憶體模型
  • 編譯和解釋啟動並執行概念

 

一、JVM啟動流程:

JVM啟動時,是由java命令/javaw命令來啟動的。

二、JVM基本結構:

JVM基本結構圖:

《深入理解Java虛擬機器(第二版)》中的描述是下面這個樣子的:

 

Java中的記憶體配置:

Java程式在運行時,需要在記憶體中的分配空間。為了提高運算效率,就對資料進行了不同空間的劃分,因為每一片地區都有特定的處理資料方式和記憶體管理方式。

具體劃分為如下5個記憶體空間:(非常重要)

  • :存放局部變數
  • :存放所有new出來的東西
  • 方法區:被虛擬機器載入的類資訊、常量、靜態常量等。
  • 程式計數器(和系統相關)
  • 本地方法棧

1、程式計數器:

每個線程擁有一個PC寄存器

線上程建立時建立

指向下一條指令的地址

執行本地方法時,PC的值為undefined

2、方法區: 

儲存裝載的類資訊

  類型的常量池

  欄位,方法資訊

  方法位元組碼

通常和永久區(Perm)關聯在一起

3、堆記憶體:

和程式開發密切相關

應用系統對象都儲存在Java堆中

所有線程共用Java堆

對分代GC來說,堆也是分代的

GC管理的主要區域

現在的GC基本都採用分代收集演算法,如果是分代的,那麼堆也是分代的。如果堆是分代的,那堆空間應該是下面這個樣子:

是堆的基本結構,在之後的文章中再進行詳解。

4、棧記憶體:

  • 線程私人,生命週期和線程相同
  • 棧由一系列幀組成(因此Java棧也叫做幀棧)
  • 幀儲存一個方法的局部變數、運算元棧、常量池指標
  • 每一次方法調用建立一個幀,並壓棧

解釋:

Java虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被調用的時候都會建立一個棧幀,用於儲存局部變數表、操作棧、動態連結、方法出口等資訊。每一個方法被調用直至執行完成的過程就對應著一個棧幀在虛擬機器中從入棧到出棧的過程。

在Java虛擬機器規範中,對這個地區規定了兩種異常情況:

    (1)如果線程請求的棧深度太深,超出了虛擬機器所允許的深度,就會出現StackOverFlowError(比如無限遞迴。因為每一層棧幀都佔用一定空間,而 Xss 規定了棧的最大空間,超出這個值就會報錯)

    (2)虛擬機器棧可以動態擴充,如果擴充到無法申請足夠的記憶體空間,會出現OOM

 

4.1  Java棧之局部變數表:包含參數和局部變數

    局部變數表存放了基礎資料型別 (Elementary Data Type)、對象引用和returnAddress類型(指向一條位元組碼指令的地址)。其中64位長度的long和double類型的資料會佔用2個局部變數空間(slot),其餘資料類型只佔用1個。局部變數表所需的記憶體空間在編譯期間完成分配。

例如,我寫出下面這段代碼:

 1 package test03; 2  3 /** 4  * Created by smyhvae on 2015/8/15. 5  */ 6 public class StackDemo { 7      8     //靜態方法 9     public static int runStatic(int i, long l, float f, Object o, byte b) {10         return 0;11     }12 13     //執行個體方法14     public int runInstance(char c, short s, boolean b) {15         return 0;16     }17 18 }

 

上方代碼中,靜態方法有6個形參,執行個體方法有3個形參。其對應的局部變數表如下:

上方表格中,靜態方法和執行個體方法對應的局部變數表基本類似。但有以下區別:執行個體方法的表中,第一個位置存放的是當前對象的引用。

 

4、2  Java棧之函數調用組成棧幀:

方法每次被調用的時候都會建立一個棧幀,例如下面這個方法:

public static int runStatic(int i,long l,float  f,Object o ,byte b){       return runStatic(i,l,f,o,b);}

 

當它每次被調用的時候,都會建立一個幀,方法調用結束後,幀出棧。如所示:

 

4.3  Java棧之運算元棧

Java沒有寄存器,所有參數傳遞都是使用運算元棧

例如下面這段代碼:

    public static int add(int a,int b){        int c=0;        c=a+b;        return c;    }

 

壓棧的步驟如下:

  0:   iconst_0 // 0壓棧

  1:   istore_2 // 彈出int,存放於局部變數2

  2:   iload_0  // 把局部變數0壓棧

  3:   iload_1 // 局部變數1壓棧

  4:   iadd      //彈出2個變數,求和,結果壓棧

  5:   istore_2 //彈出結果,放於局部變數2

  6:   iload_2  //局部變數2壓棧

  7:   ireturn   //返回

如果計算100+98的值,那麼運算元棧的變化如所示:

 

 

4.4  Java棧之棧上分配:

小對象(一般幾十個bytes),在沒有逃逸的情況下,可以直接分配在棧上

直接分配在棧上,可以自動回收,減輕GC壓力

大對象或者逃逸對象無法棧上分配

棧、堆、方法區互動:

 

 

三、記憶體模型:

每一個線程有一個工作記憶體。工作記憶體和主存獨立。工作記憶體存放主存中變數的值的拷貝。

當資料從主記憶體複製到工作儲存時,必須出現兩個動作:第一,由主記憶體執行的讀(read)操作;第二,由工作記憶體執行的相應的load操作;當資料從工作記憶體拷貝到主記憶體時,也出現兩個操作:第一個,由工作記憶體執行的儲存(store)操作;第二,由主記憶體執行的相應的寫(write)操作。

每一個操作都是原子的,即執行期間不會被中斷

對於普通變數,一個線程中更新的值,不能馬上反應在其他變數中。如果需要在其他線程中立即可見,需要使用volatile關鍵字作為標識。

1、可見度:

  一個線程修改了變數,其他線程可以立即知道

保證可見度的方法:

volatile

synchronized (unlock之前,寫變數值回主存)

final(一旦初始化完成,其他線程就可見)

2、有序性:

  在本線程內,操作都是有序的

  線上程外觀察,操作都是無序的。(指令重排 或 主記憶體同步延時)

3、指令重排:

指令重排:破壞了線程間的有序性:

 

 

指令重排:保證有序性的方法:

指令重排的基本原則:

程式順序原則:一個線程內保證語義的串列性

volatile規則:volatile變數的寫,先發生於讀

鎖規則:解鎖(unlock)必然發生在隨後的加鎖(lock)前

傳遞性:A先於B,B先於C 那麼A必然先於C

線程的start方法先於它的每一個動作

線程的所有操作先於線程的終結(Thread.join())

線程的中斷(interrupt())先於被中斷線程的代碼

對象的建構函式執行結束先於finalize()方法

 

四、解釋運行和編譯啟動並執行概念:

解釋運行:

解釋執行以解釋方式運行位元組碼

解釋執行的意思是:讀一句執行一句

編譯運行(JIT):

將位元組碼編譯成機器碼

直接執行機器碼

運行時編譯

編譯後效能有數量級的提升

編譯啟動並執行效能優於解釋運行。

Java虛擬機器詳解02----JVM記憶體結構

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.