1. 程式人生 > >【JVM從小白學成大佬】2.Java虛擬機器執行時資料區

【JVM從小白學成大佬】2.Java虛擬機器執行時資料區

目錄

  • 1.執行時資料區介紹
  • 2.堆(Heap)
    • 是否可能有兩個物件共用一段記憶體的事故?
  • 3.方法區(Method Area)
  • 4.程式計數器(Program Counter Register)
  • 5.虛擬機器棧(VM Stack)
  • 6.本地方法棧(Native Method Stack)
  • 7.擴充套件知識點
    • 7.1 棧上分配和逃逸分析
    • 7.2 棧幀
  • 8.執行時資料區腦圖

這裡我們先說句題外話,相信大家在面試中經常被問到介紹Java記憶體模型,我在面試別人時也會經常問這個問題。但是,往往都會令我比較尷尬,我還話音未落,面試者就會“背誦”一段(Java虛擬機器時有堆、方法去、虛擬機器棧,吧啦吧啦。。。),估計心裡還一臉自豪的想幸好哥提前在網上搜過,早有準備。每每這個時候,我都不忍心打斷,因為“背誦”的真的太順暢了!

這也怪不得面試者,首先Java虛擬機器方面的知識,對中高階程式猿來說,工作中正面接觸Java虛擬機器的東西不多。其次,這個其次咱得好好嘮嘮,網上搜個Java記憶體模型,度娘推的第一頁大都是介紹Java執行時資料區的,起到了一定的誤導作用,大寫的尷尬。

本篇將給各位小夥伴先詳細介紹Java執行時資料區的組成,Java記憶體模型也是虛擬機器裡面的重點,後面會單獨抽出一篇來進行介紹。

1.執行時資料區介紹

程式執行所需的記憶體空間,有些是不能在編譯期就能確定,得要在執行期根據實際執行狀況動態地在系統中建立。Java虛擬機器在執行Java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域都有各自的用途,以及建立和銷燬的時間,有的區域隨著虛擬機器程序的啟動而存在,有些區域則依賴使用者執行緒的啟動和結束而建立和銷燬。

如圖所示,堆和方法區是所有執行緒共享的公共區域,堆和方法區所佔的記憶體空間是由JVM負責管理的,在該區域內的記憶體分配是由HotSpot的記憶體管理模組維護的,而記憶體的釋放工作則由垃圾收集器自動完成。虛擬機器棧、本地方法棧、程式計數器是執行緒的私有區域,每個執行緒都關聯著唯一的棧和程式計數器,並僅能使用屬於自己的那份棧空間和程式計算器來執行程式。

2.堆(Heap)

對於大多數應用來說,Java堆(Java Heap)是Java虛擬機器所管理的記憶體中最大的一塊。堆是可供各個執行緒共享的執行時記憶體區域,在虛擬機器啟動的時候就被建立。此記憶體區域的唯一目的就是存放物件例項,幾乎所有的物件例項都在這裡分配記憶體。這一點在Java虛擬機器規範中的描述就是:所有的物件例項以及陣列物件都要在堆上分配。但是隨著JIT編譯器的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙的變化發生,所有的物件都分配在堆上也漸漸變得不是那麼“絕對”了。

Java堆的容量可以是固定的,也可以隨著程式執行的需求動態擴充套件,並在不需要過多空間時自動收縮。Java堆可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可。如果在堆中沒有記憶體完成例項分配,並且堆也無法再擴充套件時,將會丟擲OutOfMemoryError異常。

Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱做“GC堆”(Garbage Collected Heap)。從記憶體回收的角度來看,由於現在收集器基本都採用分代收集演算法,Java虛擬機器將堆劃分為新生代和老年代。其中,新生代又被分為Eden區,以及兩個大小相同的Survivor區(From Survivor,To Survivor)。預設情況下,Java虛擬機器採取的是一種動態分配的策略(JVM引數-XX:+UsePSAdaptiveSurvivorSizePolicy),根據生成物件的速率,以及Survivor區的使用情況,動態調整Eden區和Survivor區的比例。也可以通過引數(SurvivorRatio)來調整這個比例,SurvivorRatio這個引數就是新生代中Eden區與Survivor區的容量比值,預設是8,代表Eden:Survivor=8:1。

是否可能有兩個物件共用一段記憶體的事故?

當呼叫new指令時,會在Eden區劃出一塊作為儲存物件的記憶體。由於堆空間是執行緒共享的,因此直接在這裡邊劃空間是需要進行同步的。否則,將有可能出現兩個物件共用一段記憶體的事故。解決方法就是,Java堆中可能劃出多個執行緒私有的分配緩衝區TLAB(Thread Local Allocation Buffer,對應的虛擬機器引數-XX:+UseTLAB,預設開啟)。

具體來說,每個執行緒可以向Java虛擬機器申請一段連續記憶體,比如2048位元組,作為執行緒私有的TLAB。這個操作需要加鎖,執行緒需要維護兩個指標(實際上可能更多,但重要也就兩個),一個指向TLAB中空餘記憶體的起始位置,一個則指向TLAB末尾。接下來的new指令,便可以直接通過指標加法(bump the pointer),也有人叫做指標碰撞來實現,即把指向空餘記憶體位置的指標加上所請求的位元組數。如果加法後空餘記憶體指標的值仍小於或等於指向末尾的指標,則代表分配成功。否則,TLAB已經沒有足夠的空間來滿足本次新建操作。這個時候,便需要當前執行緒重新申請新的TLAB。

3.方法區(Method Area)

方法區與堆一樣是執行緒共享的,在虛擬機器啟動的時候建立,方法區可視為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的應該是與Java堆區分開來。

方法區類似於傳統語言編譯後的程式碼儲存區域,它儲存每個類的結構資訊,如:

  • 常量池
  • 方法資料
  • 方法和建構函式的位元組碼
  • 類、例項、介面初始化時用到的特殊方法

備註:《深入理解Java虛擬機器》裡將方法區歸納為用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。

Java虛擬機器規範對方法區的限制非常寬鬆,除了和Java堆一樣不需要連續的記憶體和可以選擇固定大小或者可擴充套件外,還可以選擇不實現垃圾收集。這區域的記憶體回收目標主要是針對常量池的回收和對型別的解除安裝。

4.程式計數器(Program Counter Register)

Java虛擬機器可以支援多條執行緒同時執行,每一條Java虛擬機器執行緒都有自己的程式計數器。在任意時刻,一條Java虛擬機器執行緒只會執行一個方法的程式碼,這個正在被執行緒執行的方法稱為該執行緒的當前方法(current methon)。如果這個方法不是native的,那程式計數器儲存的就是Java虛擬機器正在執行的位元組碼指令的地址。如果該方法是native方法,那程式計數器的值為空(undefined)。程式計數器的容量至少應當儲存一個returnAddress型別的資料或者一個與平臺相關的本地指標的值。

程式計數器是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器。此記憶體區域是唯一一個在Java虛擬機器規範中沒有規定任何OutOfMemoryError情況的區域。

5.虛擬機器棧(VM Stack)

每一條Java虛擬機器執行緒都有自己私有的Java虛擬機器棧,它的生命週期與執行緒相同。虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法在執行的同時都會建立一個棧幀(stack frame)用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。每一個方法從呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器棧中入棧到出棧的過程。

Java虛擬機器棧可能發生如下異常情況:

  • 如果執行緒請求分配的棧容量超過Java虛擬機器棧允許的最大容量,Java虛擬機器將會丟擲一個StackOverflowError異常。
  • 如果Java虛擬機器棧可以動態擴充套件,並且在嘗試擴充套件的時候無法申請到足夠的記憶體,或者在建立新的執行緒時沒有足夠的記憶體區建立對應的虛擬機器棧,那Java虛擬機器將會丟擲一個OutOfMemoryError異常

6.本地方法棧(Native Method Stack)

本地方法棧與虛擬機器棧所發揮的作用是非常相似的,它們之間的區別不過是虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,而本地方法棧則為虛擬機器使用到的native方法服務。

Java虛擬機器規範允許本地方法棧實現成固定大小或者根據計算來動態擴充套件和收縮。如果採用固定大小的本地方法棧,那麼每一個執行緒的本地方法棧容量可以在建立棧的時候獨立選定。

與虛擬機器棧一樣,本地方法棧區域也會丟擲StackOverflowError和OutOfMemoryError異常。

7.擴充套件知識點

7.1 棧上分配和逃逸分析

在棧中分配的基本思路是這樣的:分析區域性變數的作用域僅限於方法內部,則JVM直接在棧幀內分配物件空間,避免在堆中分配。這個分析過程稱為逃逸分析(也有叫逸出分析),而棧幀內分配物件的方式稱為棧上分配。

這樣做的目的是減少新生代的收集次數,間接提高JVM效能。虛擬機器是允許堆逃逸分析開關進行配置的,從Sun Java 6u23以後,HotSpot預設開啟逃逸分析。

7.2 棧幀

棧幀是用於支援虛擬機器進行方法呼叫和方法執行的資料結構,它是虛擬機器執行時資料區中的虛擬機器棧的棧元素。棧幀儲存了方法的區域性變量表、運算元棧、動態連線和方法返回地址等資訊。每一個方法從呼叫開始至執行完成的過程,都對應著一個棧幀在虛擬機器棧裡面從入棧到出棧的過程。

在編譯程式程式碼的時候,棧幀中需要多大的區域性變量表,多深的運算元棧都已經完全確定了,並且寫入到方法表的Code屬性之中。因此一個棧幀需要分配多少記憶體,不會收到程式執行期變數資料的影響,而僅僅取決於具體的虛擬機器實現。

一個執行緒中的方法呼叫鏈可能會很長,很多方法都同時處於執行狀態。對於執行引擎來說,在活動執行緒中,只有位於棧頂的棧幀才是有效的,稱為當前棧幀(Current Stack Frame),與這個棧幀相關聯的方法稱為當前方法(Current Method)。執行引擎執行的所有位元組碼指令都只針對當前棧幀進行操作。棧幀的概念結構如下:

8.執行時資料區腦圖

相關推薦

JVM從小2.Java虛擬機器執行資料

目錄 1.執行時資料區介紹 2.堆(Heap) 是否可能有兩個物件共用一段記憶體的事故? 3.方法區(Method Area) 4.程式計數器(Program Counter

JVM從小4.Java虛擬機器何謂垃圾及垃圾回收演算法

在Java中記憶體是由虛擬機器自動管理的,虛擬機器在記憶體中劃出一片區域,作為滿足程式記憶體分配請求的空間。記憶體的建立仍然是由程式猿來顯示指定的,但是物件的釋放卻對程式猿是透明的。就是解放了程式猿手動回收記憶體的工作,交給垃圾回收器來自動回收。 在虛擬機器中,釋放哪些不再被使用的物件所佔空間的過程稱為垃圾收

JVM從小3.深入解析強引用、軟引用、弱引用、幻象引用

關於強引用、軟引用、弱引用、幻象引用的區別,在很多公司的面試題中經常出現,可能有些小夥伴覺得這個知識點比較冷門,但其實大家在開發中經常用到,如new一個物件的時候就是強引用的應用。 在java語言中,除了原始資料型別(boolean、byte、short、char、int、float、double、long)

JVM從小5.垃圾收集器及記憶體分配策略

前面介紹了垃圾回收演算法,接下來我們介紹垃圾收集器和記憶體分配的策略。有沒有一種牛逼的收集器像銀彈一樣適配所有場景?很明顯,不可能有,不然我也沒必要單獨搞一篇文章來介紹垃圾收集器了。熟悉不同收集器的優缺點,在實際的場景中靈活運用,才是王道。 在開始介紹垃圾收集器前,我們可以劇透幾點: 根據不同分代的特點,收

JVM從小6.建立物件及物件的訪問定位

《JVM從小白學成大佬》系列推出到現在,收到了很多小夥伴的好評,也收到了一些小夥伴的建議,在此表示感謝。 有幾個小夥伴提出了希望出一篇介紹物件的建立及訪問,猿人谷向來是沒有原則的,小夥們要求啥,咱就盡力滿足,畢竟文章就是對自己學習的一個總結及和各位小夥伴交流學習的機會。話不多說,直接開擼! 1 建立物件 在J

Java虛擬機器JVM原始碼):JDK10對Java虛擬機器執行資料的劃分(詳細圖解)

Java虛擬機器執行時資料區 為什麼要研究這個,因為JDK都已經發布到10了,必須要更新自己對Java虛擬機器新的認識。 一、執行時資料區的劃分 1.1 官方劃分 關於JDK10對執行時資料區的劃分,在官方文件說的非常清楚。 學習技術,一定要學會看第一手資料。 Ja

迎戰無碳小車從小入門為第二階段

真正要做的階段了,可是自己毫無頭緒,我也不怕洩露什麼創意之類的。自己在這裡把過程記錄下來,對自己很有幫助就行了。若能幫到你就點贊就行。大家來到這個blog的目的就是交流嘛。簡單的說就是要完成一個輛S型的酷酷小車。 小車分為能量轉換系統、行走系統、轉向系統。S型小

JVM之記憶體與垃圾回收篇執行資料概述及執行

# 執行時資料區概述及執行緒 ## 前言 本節主要講的是執行時資料區,也就是下圖這部分,它是在類載入完成後的階段 ![](https://img2020.cnblogs.com/blog/1542615/202007/1542615-20200713210432252-2095338296.png)

JVM筆記1-- 執行資料

[TOC] # (一)java記憶體區域管理 C/C++每一個new操作都需要自己去delete/free,而java裡面有虛擬機器自動管理記憶體,不容易出現記憶體洩漏或者溢位的問題,但是不容易出現不代表不出現,瞭解虛擬機器怎麼使用和管理記憶體是十分重要的是,對程式優化或者問題排查有幫助。 執行時區域主要

深入理解JVM(一):執行資料

深入理解JVM(一):執行時資料區 執行時資料區 JVM在執行java程式的過程中,會把記憶體分為幾個不同的資料區域,如上圖所示。 程式計數器 雖然圖片中程式計數器所佔的面積比較大,但實際上程式計數器所佔的記憶體非常小,也是唯一一塊在所有JVM中都沒有規定OOM的區

JVMjvm執行資料

1、程式計數器 程式計數器(Program Counter Register)是一塊較小的記憶體空間,它的作用可以看做是當前執行緒所執行的位元組碼的行號指示器。在虛擬機器的概念模型裡(僅是概念模型,各種虛擬機器可能會通過一些更高效的方式去實現),位元組碼直譯器工作時就是通過改變這個

JVM 執行資料詳解

       1、PC暫存器(執行緒獨有):全稱是程式計數暫存器,它記載著每一個執行緒當前執行的JAVA方法的地址,如果是當前執行的是native方法,則程式計數器會是一個空地址。它的作用就是用來支援多執行緒,執行緒的阻塞、恢復、掛起等一系列操作。這

執行資料組成_深入JVM(一)

執行時資料區         JVM在執行java程式時,會把其所管理的記憶體劃分成多個區域,每個區域都有不同用途,每個區域的建立和銷燬時間也不同.這每塊記憶體都屬於執行時資料區域.         這些區域有程

JVM執行資料-堆

基本概念     堆,jvm程序啟動時建立,為jvm程序的所有執行緒共享的區域,所有Java物件和陣列(jdk8+也包含字串常量)均在堆上分配記憶體,所以堆的大小決定了能存放多少Java物件,當堆滿或者無法容納新建立的物件時,則需要通過垃圾收集器進行垃

JVM執行資料-方法

基本概念      在hotspot jvm實現當中,在jdk8以前,方法區的實現為PermGen,即永久代,主要存放類的資訊,方法的資訊,常量池,靜態變數,符號解析;而jdk8+,則去除了PermGen,使用元空間MetaSpace代替,元空間使用的

JVM(一):JVM執行資料

由於Java程式是交由JVM執行的,所以我們在談Java記憶體區域劃分的時候事實上是指JVM記憶體區域劃分。在討論JVM記憶體區域劃分之前,先來看一下Java程式具體執行的過程: 如上圖所示,首先Java原始碼檔案(.java字尾)會被Java編譯器編譯為位元組碼檔案(.class字尾),

jvm 執行資料、String 常量池

私有 - 程式計數器:記錄當前執行緒所執行位元組碼的行號指示器 - 虛擬機器棧:存放了當前執行緒呼叫方法的區域性變量表、運算元棧、動態連結、方法返回值等資訊(可以理解為執行緒 的棧) - 本地方法棧:為虛擬機器使用的Native方法提供服務 共享 - Java堆

JVM執行資料

JVM在執行java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域都有各自的用途,以及建立和銷燬的時間,有的區域隨著JVM程序的啟動而存在,有的區域則依賴使用者執行緒的啟動和結束而建立和銷燬。 JVM鎖管理的記憶體將會包括以下幾個執行時資料區域: (

JVM執行資料詳解

在Java虛擬機器中,堆(Heap)是可供各條執行緒共享的執行時記憶體區域,也是供所有類例項和陣列物件分配記憶體的區域。 Java堆在虛擬機器啟動的時候就被建立,它儲存了被自動記憶體管理系統(Automatic Storage Manageme

已解決VMware Linux虛擬機器執行提示“鎖定檔案失敗,虛擬機器開啟模組snapshot失敗”的解決辦法

原因:本人因在設定虛擬機器時CD/DVD硬體連線方式將使用iso映像檔案改成了物理驅動連線,導致VMware Linux虛擬機器啟動時提示“鎖定檔案失敗...” 、 (注:期間碰到程序被佔用問題