【JVM系統學習之路】運行時數據區概述和程序計數器

2021-02-25 山間木匠

JVM系統學習之路系列演示代碼地址:https://github.com/mtcarpenter/JavaTutorial

本篇將 運行時數據區概述及線程 和 程序計數器 的知識點由於不是很多所以就一起梳理,也是為後續學習的知識點做一個鋪墊。

運行時數據區概述

運行時數據區,它是在類加載完成後的階段,如果對類加載不是很熟悉的小夥伴,可以看我上一篇文章。

當我們通過前面的:類的加載-> 驗證 -> 準備 -> 解析 -> 初始化  這幾個階段完成後,就會用到執行引擎對我們的類進行使用,同時執行引擎將會使用到我們運行時數據區,如下內存是非常重要的系統資源,是硬碟和 CPU 的中間倉庫及橋梁,承載著作業系統和應用程式的實時運行 JVM 內存布局規定了 Java 在運行過程中內存申請、分配、管理的策略,保證了 JVM 的高效穩定運行。不同的 JVM 對於內存的劃分方式和管理機制存在著部分差異。結合 JVM 虛擬機規範,來探討一下經典的 JVM 內存布局。

我們通過磁碟或者網絡 IO 得到的數據,都需要先加載到內存中,然後 CPU 從內存中獲取數據進行讀取,也就是說內存充當了 CPU 和磁碟之間的橋梁

首先還是一張運行時數據區的完整圖:Java 虛擬機定義了若干種程序運行期間會使用到的運行時數據區,其中有一些會隨著虛擬機啟動而創建,隨著虛擬機退出而銷毀。另外一些則是與線程一一對應的,這些與線程對應的數據區域會隨著線程開始和結束而創建和銷毀。

灰色的為單獨線程私有的,紅色的為多個線程共享的。即:

線程間共享:堆、堆外內存(永久代或元空間、代碼緩存)image.png線程

線程是一個程序裡的運行單元。JVM 允許一個應用有多個線程並行的執行。在 Hotspot JVM 裡,每個線程都與作業系統的本地線程直接映射。

當一個 Java 線程準備好執行以後,此時一個作業系統的本地線程也同時創建。Java 線程執行終止後,本地線程也會回收。

作業系統負責所有線程的安排調度到任何一個可用的 CPU 上。一旦本地線程初始化成功,它就會調用 Java 線程中的run() 方法。

JVM系統線程

如果你使用 console 或者是任何一個調試工具,都能看到在後臺有許多線程在運行。這些後臺線程不包括調用 public static void main(String[]) 的 main 線程以及所有這個main線程自己創建的線程。這些主要的後臺系統線程在 Hotspot JVM 裡主要是以下幾個:

虛擬機線程:這種線程的操作是需要 JVM 達到安全點才會出現。這些操作必須在不同的線程中發生的原因是他們都需要JVM達到安全點,這樣堆才不會變化。這種線程的執行類型包括 "stop-the-world" 的垃圾收集,線程棧收集,線程掛起以及偏向鎖撤銷。周期任務線程:這種線程是時間周期事件的體現(比如中斷),他們一般用於周期性操作的調度執行。GC 線程:這種線程對在 JVM 裡不同種類的垃圾收集行為提供了支持。編譯線程:這種線程在運行時會將字節碼編譯成到本地代碼。信號調度線程:這種線程接收信號並發送給 JVM,在它內部通過調用適當的方法進行處理。程序計數器(PC Register) 介紹

JVM 中的 程序計數寄存器 ( Program Counter Register )中,Register 的命名源於 CPU 的寄存器,寄存器存儲指令相關的現場信息。CPU 只有把數據裝載到寄存器才能夠運行。這裡,並非是廣義上所指的物理寄存器,或許將其翻譯為 PC計數器(或指令計數器)會更加貼切(也稱為程序鉤子),並且也不容易引起一些不必要的誤會。JVM 中的 PC 寄存器是對物理PC寄存器的一種抽象模擬。程序計數器是一塊很小的內存空間,幾乎可以忽略不記。也是運行速度最快的存儲區域。在 JVM 規範中,每個線程都有它自己的程序計數器,是線程私有的,生命周期與線程的生命周期保持一致。任何時間一個線程都只有一個方法在執行,也就是所謂的當前方法。程序計數器會存儲當前線程正在執行的 Java 方法的 JVM 指令地址;或者,如果是在執行 native 方法,則是未指定值(undefned)。它是程序控制流的指示器,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令。它是唯一一個在 Java 虛擬機規範中沒有規定任何 outotMemoryError 情況的區域。

作用

PC寄存器 用來存儲指向下一條指令的地址,也即將要執行的指令代碼。由執行引擎讀取下一條指令。

舉例說明

通過寫一個簡單的代碼演示:

/**程序計數器
 */
public class PCRegisterTest {
    public static void main(String[] args) {
        int i = 10;
        int j = 20;
        int k = i + j;
    }
}

將上面的 java 文件編譯成字節碼文件,然後執行  ` javap -c PCRegisterTest.class``  ,通過控制臺查看 ,發現在字節碼的左邊有一個行號標識,它其實就是指令地址,用於指向當前執行到哪裡。

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1
       3: bipush        20
       5: istore_2
       6: iload_1
       7: iload_2
       8: iadd
       9: istore_3
      10: return
}

通過PC寄存器,我們就可以知道當前程序執行到哪一步了

兩個常見問題使用PC寄存器存儲字節碼指令地址有什麼用呢?

因為 CPU 需要不停的切換各個線程,這時候切換回來以後,就得知道接著從哪開始繼續執行。JVM的字節碼解釋器就需要通過改變 PC 寄存器 的值來明確下一條應該執行什麼樣的字節碼指令。

PC寄存器為什麼被設定為私有的?

我們都知道所謂的多線程在一個特定的時間段內只會執行其中某一個線程的方法,CPU 會不停地做任務切換,這樣必然導致經常中斷或恢復,如何保證分毫無差呢?為了能夠準確地記錄各個線程正在執行的當前字節碼指令地址,最好的辦法自然是為每一個線程都分配一個PC寄存器 ,這樣一來各個線程之間便可以進行獨立計算,從而不會出現相互幹擾的情況。由於 CPU 時間片輪限制,眾多線程在並發執行過程中,任何一個確定的時刻,一個處理器或者多核處理器中的一個內核,只會執行某個線程中的一條指令。這樣必然導致經常中斷或恢復,如何保證分毫無差呢?每個線程在創建後,都會產生自己的程序計數器和棧幀,程序計數器在各個線程之間互不影響。

CPU時間片

CPU 時間片即 CPU 分配給各個程序的時間,每個線程被分配一個時間段,稱作它的時間片。在宏觀上:我們可以同時打開多個應用程式,每個程序並行不悖,同時運行。但在微觀上:由於只有一個 CPU,一次只能處理程序要求的一部分,如何處理公平,一種方法就是引入時間片,每個程序輪流執行。

總結

本篇回顧,第一部分為運行時數據區,第二部分就是程序計數器。JVM 能高效穩定運行 ,主要是 JVM 內存布局規定了 Java 在運行過程中內存申請、分配、管理的策略。接下來會通過很多的章節講述運行時數據區下的各個區。還需要了解PC寄存器 用來存儲指向下一條指令的地址,也即將要執行的指令代碼。由執行引擎讀取下一條指令。

歡迎關注公眾號 山間木匠 , 我是小春哥,從事 Java 後端開發,會一點前端、通過持續輸出系列技術文章以文會友,如果本文能為您提供幫助,歡迎大家關注、在看、 點讚、分享支持,我們下期再見!



相關焦點

  • Java虛擬機系列一:一文搞懂 JVM 架構和運行時數據區
    本文分為兩大部分,將分別為大家介紹 JVM 的整體架構和運行時數據區,這兩部分的依據均是《Java 虛擬機規範》,而不針對任何特定的 JVM 具體實現版本。一、Java 虛擬機架構 (JVM Architecture)在我看來,不管學習什麼樣的知識或技術,首先要做的就是從全局上去認識它,這樣才能避免盲人摸象,事倍功半的情況發生。
  • 這一定是全網寫JVM最好的文章之一—JVM運行時數據區
    Java內存區域根據上圖可以知道,方法區和堆是一個顏色,虛擬機棧和本地方法棧和程序計數器是一個顏色 所以,方法區和堆是線程共享的,虛擬機棧和本地方法棧和程序計數器是線程私有的通過上圖,可知Java內存區域包括運行時數據區和執行引擎和本地庫接口和本地方法庫運行時數據區
  • JVM-概述和內存區域
    舉個例子將groovy編譯之後的class文件用jvm運行1、先配置好groovy環境方法區(永久代)在jdk8中又叫做元空間Metaspace運行時數據區概述堆內存:保存所有引用數據的真實信息;棧內存:基本類型、運算、指向堆內存的指針;方法區:所以定義的方法的信息都保存方法區中,屬於共享區
  • 想理解JVM看了這篇文章,就知道了!
    本次將對jvm有更深入的學習,我們不僅要讓程序能跑起來,而且是可以跑的更快!可以分析解決在生產環境中所遇到的各種「棘手」的問題,比如運行的應用卡住了,日誌不輸出,程序沒有反應,CPU負載突然升高,多線程應用下,如何分配線程數量等。
  • 程式設計師每日一題-jvm裡方法和方法區、棧區的二三事
    答案是D解析又是JVM相關的,那就先上一個JAVA虛擬機運行時數據區的邏輯圖JAVA虛擬機運行時數據區的邏輯圖好,對照上圖,逐項解釋A:堆區是JVM 所管理的內存中最大的一塊。線程共享,主要是存放對象實例和數組。
  • JVM中的五大內存區域劃分詳解及快速掃盲
    腦袋裡有個印象即可,帶著問題去學習。4. 運行java文件的大概流程想要運行java的源文件,必須要經過javac編譯器編譯成.class文件,也就是字節碼文件。然後通過jvm中的解釋器,解釋成特定機器上的機器碼。
  • JVM內存區域之線程私有區域
    不過,也正是java把控制內存的權力交給了java虛擬機,一旦出現內存洩漏和內存溢出方面的問題,如果不了解虛擬機是怎麼使用內存的,那排查錯誤、修正問題將會成為一項異常艱難的工作。運行時數據區:java虛擬機在執行java程序的過程中會把它所管理的內存劃分為若干個不同的數據區域。
  • JDK、JRE、JVM,是什麼關係?
    JVM(Java Virtual Machine Java虛擬機),JVM可以理解為是一個虛擬出來的計算機,具備著計算機的基本運算方式,它主要負責把 Java 程序生成的字節碼文件,解釋成具體系統平臺上的機器指令,讓其在各個平臺運行。「綜上」,從這段官網的平臺標準介紹和概念圖可以看出,我們運行程序的 JVM 是已經安裝到 JDK 中,只不過可能你開發了很久的代碼,也沒有注意過。
  • 聊到JVM(還怕面試官問JVM嗎?)
    3、JVM體系結構 ‍類裝載器子系統運行時數據區執行引擎本地方法接口垃圾收集模塊    沙箱機制就是將 Java 代碼限定在虛擬機(JVM)特定的運行範圍中,並且嚴格限制代碼對本地系統資源訪問,通過這樣的措施來保證對代碼的有效隔離,防止對本地系統造成破壞。沙箱主要限制系統資源訪問,系統資源包括CPU、內存、文件系統、網絡。不同級別的沙箱對這些資源訪問的限制也可以不一樣。所有的Java程序運行都可以指定沙箱,可以定製安全策略。
  • 面經手冊 · 第23篇《JDK、JRE、JVM,是什麼關係?》
    JRE(Java Runtime Environment Java運行環境) 是 JDK 的子集,也就是包括 JRE 所有內容,以及開發應用程式所需的編譯器和調試器等工具。JRE 提供了庫、Java 虛擬機(JVM)和其他組件,用於運行 Java 程式語言、小程序、應用程式。
  • java線程前傳——jvm內存結構、內存模型和cpu結構
    ,這個過程是少不了的一個線程肯定是要運行在一個核上的,多個線程可以運行在不同的核上,這個時候,因為緩存的存在,如果沒有同步機制,那一個線程修改了緩存的數據,另一個線程也修改了緩存的數據,這個時候這兩個線程修改後的數據都需要寫入到內存當中,就會出現問題jvm為了方便,將這些緩存抽象出來,構造了自己的內存模型,即主內存和工作內存的數據交互,即java 內存模型(jmm)
  • java垃圾回收以及jvm參數調優概述
    Java虛擬機的內存區域中,程序計數器、虛擬機棧和本地方法棧三個區域是線程私有的,隨線程生而生,隨線程滅而滅;棧中的棧幀隨著方法的進入和退出而進行入棧和出棧操作,每個棧幀中分配多少內存基本上是在類結構確定下來時就已知的,因此這三個區域的內存分配和回收都具有確定性。垃圾回收重點關注的是堆和方法區部分的內存。引用計數算法是垃圾收集器中的早期策略。
  • jvm面試系列一:java內存模型你掌握了多少?
    Java 內存模型 (Java Memory Model)JVM中用heap堆來存儲運行時數據,所有類實例和數組由堆分配內存,JVM啟動時創建堆(heap memory),在堆以外的內存叫非堆(non-heap memory).
  • JVM、GC 大串講,面試夠用了
    JVM不僅可以運行java程序,只要是能編譯成.class的文件都能運行。JRE (Java 運行時環境),包含了jvm和core lib。JDK (Java 開發工具包),它集成了jre和一些工具。比如javac.exe,java.exe,jar.exe等。大家都知道,要想執行java程序,需要安裝jdk。JVM 初識JVM其實是一種規範,它提供可以執行Java字節碼的運行時環境。
  • 一條進程的棧區、堆區、數據區和代碼區在內存中的映射
    l堆區:用於存放動態分配的對象,當你使用malloc和new等進行分配時,所得到的空間就在堆中。動態分配得到的內存區域附帶有分配信息,所以你能夠free和delete它們。本文引用地址:http://www.eepw.com.cn/article/201611/317090.html數據區:全局,靜態和常量是分配在數據區中的,數據區包括bss(未初始化數據區)和初始化數據區。注意:1)堆向高內存地址生長;2)棧向低內存地址生長;3)堆和棧相向而生,堆和棧之間有個臨界點,稱為stkbrk。
  • Java 基礎知識總結(一)之Java 概述
    每一個思路部分用到哪些語句,方法,和對象。4,代碼實現。用具體的java 語言代碼把思路體現出來。學習新技術的四點1,該技術是什麼?2,該技術有什麼特點(使用注意):3,該技術怎麼使用。,如手機中的程序;1,JDK:Java Development Kit,java 的開發和運行環境,java 的開發工具和jre。
  • JVM內存分析,程式設計師進階的必經之路
    最終還是決定更加深入地學習下JVM,同時也用自己的理解詳細地說明Java程序是如何運行的。成員變量,成員方法等等,其中方法和變量還分靜態和非靜態,這些都是存儲在方法區裡面的。除了類裡的數據,常量池也在方法區裡面。方法運行時,會從方法區進入到棧裡面;創建該類的對象時,會將創建的對象存放在堆裡面。
  • JVM超神之路:年後跳槽需要的JVM知識點,周末給你整理了一份!!!
    特別情況下我還會追加一些反映對象晉升情況和堆詳細信息的日誌,這些會單獨打到gc.log文件中用來排查問題。另外,OOM 時自動 Dump 堆棧,我一般也會進行配置。2、常用的調優工具有哪些?JDK內置的命令行:jps(查看jvm進程信息)、jstat(監視jvm運行狀態的,比如gc情況、jvm內存情況、類加載情況等)、jinfo(查看jvm參數的,也可動態調整)、jmap(生成dump文件的,在dump的時候會影響線上服務)、jhat(分析dump的,但是一般都將dump導出放到mat上分析)、jstack(查看線程的)。