jvm特性與原理---------->jvm運行時數據區分區

1.概述:

  •  內存分區:JVM會把本身所管理的全部內存區域進行分區。
  •  各個區域的服務對象
  •  各個區域中分別存放了什麼內容
    •  存放的數據是如何建立的
    •  這些數據在各個區域中存放,存儲的佈局是什麼樣的
    •  如何訪問存放在不一樣內存區域的數據
  •  各個區域的建立和銷燬時間
    •  隨着進程的啓動和結束而建立和銷燬
    •  隨着線程的啓動和結束而建立和銷燬  
  •  各個區域服務過程當中可能產生的問題(異常)
    • 各個區域中可能產生的異常
    • 如何解決上述各類異常  

 

2.JVM內存分區

 

3.JVM內存各個區域的比較

 

 

JVM內存分區(JVM運行時數據區)java

 

全部線程共享的數據區shell

(有效範圍:整個進程)數組

線程隔離的數據區(線程私有內存)安全

(有效範圍:單個獨立線程)佈局

 

方法區spa

Method area線程

 

(又稱GC)指針

Heap對象

 

程序計數器blog

Program Counter Register

虛擬機棧

VM Stack

 

本地方法棧

Native Method Stack

 

概述

  • 方法區能夠被垃圾回收器管理,也能夠不被垃圾回收器管理


  • 該部份內存是垃圾收集器管理的主要區域
  • 堆還能夠細分爲新生代和老年代
  • 或者分爲Eden空間+From Survivor+ToSurvivor空間
  • Java堆既能夠是固定的大小,也能夠設置成可擴展的,經過-Xmx和-Xms控制
     相似於虛擬機棧
  • 和虛擬機棧惟一的不一樣之處在於虛擬機棧服務於java方法,而本地方法棧服務於Native方法
  • Native方法能夠是任何語言(如C、Python、shell程序)
  • 有些虛擬機中直接將本地方法棧和虛擬機棧合二爲一

佔用內存空間大小

  • 方法區能夠是固定的大小;也能夠設置成可擴展的
  • 方法區內存能夠出於物理上不連續的空間中
  •  對於大多數具體應用而言,Heap是JVM所管理的內存中最大的一塊;
  • Java堆既能夠是固定的大小,也能夠設置成可擴展的,經過-Xmx和-Xms控制
  • Java堆能夠處於物理上不連續的空間中,只要邏輯上是連續的便可

較小內存空間

和具體的java成員方法代碼有關;

和該線程包含的成員方法個數有關。

和具體的Native方法有關

Native方法能夠是其餘語言(Pythonshell)

服務對象

一個進程中的全部線程

(全部線程共享方法區)

一個進程中的全部線程

全部線程共享Heap區域內存

當前線程

(每條線程都有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲)

當前線程

每一個線程都有本身獨有的該部份內存

 

當前線程

每一個線程都有本身獨有的該部份內存

一個本地方法棧中有多個棧幀

Native method stack=棧幀1+棧幀2+...

每一個棧幀服務於單個的Native方法

生命週期

 

Jvm啓動時建立

其生命週期與當前線程相同

其生命週期與當前線程相同

其生命週期與當前線程相同

存放內容

+

功能

v 存放內容:

概述:

方法區用於存儲已經被JVM加載的類信息+常量+靜態變量+即時編譯器編譯後的代碼

 

詳細解釋:

  1. 類信息=類的版本+字段+方法+接口等描述信息
  2. 運行時常量池=編譯期生成的各類字面量和符號引用(如類的符號引用)
  3. 編譯期間會將一些數據放入運行時常量池中,運行期間也能夠把一些數據放入運行時常量池中,如Stringintern()方法

 

v 功能:

v 存放內容:

用於存放對象實例

用於存放全部數組

v 存放內容:

指示當前線程將要執行的下一條字節碼指令的行號

v 功能:

做爲當前線程所執行的字節碼的行號指示器,字節碼解釋器工做時經過改變這個計數器的值來選取下一條須要執行的字節碼指令

v 存放內容:

虛擬機棧=棧幀1+棧幀2+....

線程中的每一個java成員方法對應於一個棧幀

棧幀=局部變量表+操做數棧+動態連接+方法出口

局部變量表=8種基本數據類型+對象引用(地址)+returnAddress(一條字節碼指令的地址)

局部變量表是在編譯期間生成的

v 功能:

每一個java成員方法執行過程當中都會建立一個棧幀(stack frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每個java方法從調用到執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。

一個虛擬機棧中有多個棧幀(stack frame),

VM Stack=棧幀1+棧幀2+...

每一個棧幀服務於單個的java方法。

v 存放內容:

v 功能:

每一個Native成員方法執行過程當中都會建立一個棧幀(stack frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每個Native方法從調用到執行完成的過程,就對應着一個棧幀在本地方法棧棧中入棧到出棧的過程。

 

 

各個區域存放的數據是如何建立的?

 

 

  • JVM中如何建立一個類對象實例?(僅限於普通java對象,不包括數組和class對象)

Step1JVM遇到一個new指令時,

Step2,先去「方法區」的運行時「常量池」中定位到一個類的符號引用,

Step3,檢查step2中找到的該類的符號引用所表明的實際的類是否已被加載、解析和初始化過,若是沒有則執行相應的「類的加載過程」

Step4,該類的加載檢查經過以後,JVM纔開始爲該類的新生對象分配內存(在heap內存中分配出一部分給新生對象)

至於具體的內存分配過程,則要看JVM所管理的堆內存空間的連續性是怎樣的:若是堆內存是規整的,則經過「指針碰撞」方法爲新建對象分配內存;若是堆內存是不規整的,只能經過「空閒列表」分配堆內存空間,而且更新「空閒列表」

Step5,由step4可知,從JVM堆內存空間中分配一部分給new出的對象,其分配方法和堆內存空間是否規整有關,而堆內存空間是否規整又和垃圾回收機制有關。若是JVM中的垃圾回收期帶有壓縮整理功能,則堆內存空間是規整的,可使用「指針碰撞」方法分配堆內存空間給new出的對象,不然就只能使用維護「空閒列表」的方法分配堆內存。

Step6,給new出的對象分配好堆內存空間以後,還要對該對象進行必要的設置(即爲對象添加「對象頭」),如該對象是哪一個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡信息等。這些信息存放在對象的對象頭中。

Step7,執行init方法,初始化新建的對象。

Step8,將新生的對象的引用入棧(虛擬機棧)

  • 在堆內存空間分配內存給new出的對象時,容易出現線程安全問題,如何避免該類問題的發生?

方法一,爲分配堆內存的操做加上同步處理

方法二,爲單個線程分配獨立的堆內存空間TLAB(本地線程分配緩衝),而後執行每一個線程時,在TLAB上分配內存給new出的對象

 

 

數據在這些區域存儲的佈局是怎樣的?

 

 

  • 對象在heap內存中的存儲佈局:

對象頭(Header+實例數據(Instance Data+對齊填充(padding

  1. 對象頭=對象自身的運行時數據+類型指針+(數組長度)

ü 對象自身的運行時數據=哈希碼+GC分代年齡+鎖狀態標誌+線程持有的鎖+偏向線程ID+偏向時間戳  

ü 類型指針:這是一個指針,其中存儲了一個地址。這個地址指向JVM內存的「方法區」中的某個部分,「方法區」該部份內容存放的是該對象實例對應的類信息

ü (數組長度)

  1. 實例數據
  2. 對齊填充

如何使用存放在這些區域的數據?

  • 對象的訪問定位:
  1. Java程序要經過「虛擬機棧」上的「局部變量表」中的對象引用中存儲的對象實例的地址來操做JVM堆上存儲的實例對象。
  2. 經過上述reference數據訪問堆中對象的具體方式有兩種:使用句柄和直接指針。

使用句柄:堆內存-->句柄池;句柄=對象數據+指向類信息的指針

直接指針:沒有句柄池

服務過程當中可能產生的異常

OutOfMemoryError:若是方法區中沒有足夠的內存存放相應的類信息或+常量+靜態變量+即時編譯器編譯數據,而且方法區再也沒法擴展時,就會拋出該異常

OutOfMemoryError若是堆中沒有足夠的剩餘內存來存放新的實例對象,而且堆也沒法再擴展時,就會拋出該異常

不會產生異常

(這是JVM中惟一一個不會產生OutofMemoryError的區域)

u StackOverflowError若是線程請求的棧深度大於JVM所容許的深度,就拋出該異常

u OutOfMemoryError若是虛擬機棧能夠動態擴展,可是在擴展時沒法申請到足夠的內存,就會拋出該異常

u StackOverflowError若是線程請求的棧深度大於JVM所容許的深度,就拋出該異常

u OutOfMemoryError若是虛擬機棧能夠動態擴展,可是在擴展時沒法申請到足夠的內存,就會拋出該異常

相關文章
相關標籤/搜索