深入理解Java虛擬機(第三版)01-Java虛擬機運行時數據區介紹

微信公衆號:[老張聊天]
又穩又重的老碼農,分享工作生活經驗。
風趣幽默的段子手,總結編程專業知識。
希望能用通俗易懂的語言,給小夥伴帶來收穫~
[如果覺得對您有幫助,歡迎關注,轉發,點贊!]

一、開篇說明

老張最近在看多線程、鎖的源碼,但是在學習源碼過程中,發現需要對Java虛擬機,以及JMM等有一個全面的理解,能更快的吸收源碼中的知識。所以先更新Java虛擬機專欄內容。

之前沒有接觸過的小夥伴也不用怕不用慫,其實這部分知識,大部分還是理論。推薦先看一遍視頻學習,對整體有印象和概念後,再來看書補充細節,多自己畫圖記憶。最後像老張一樣,把學到的知識點鞏固一下,整理成博客,最後按照自己的思路、自己的話能讓別人聽懂,就證明對這部分知識理解透徹了。

視頻可以在B站搜索查看,有很多免費教程。

大家都知道Java程序可以轉爲字節碼在Java虛擬機上運行,但是Java虛擬機有HotSpot,OpenJDK等等,一套字節碼可以在多套虛擬機上運行,就是因爲虛擬機受到《Java虛擬機規範》約束。《Java虛擬機規範》相當於API,而不同的Java虛擬機實現相當於Impl。

除了Java語言,Scala語言等也可以在Java虛擬機上運行,不同的語言都可以編譯成字節碼。Java受到《Java語言規範》的約束,規定如何聲明類,創建方法等等。同理Scala語言也有《Scala語言規範》。

Java編譯器同時受到《Java虛擬機規範》與《Java語言規範》的約束。

以下大部分內容基於周志明的《深入理解Java虛擬機 第三版》,第三版相比前面更新了很多知識點。pdf版本已經上傳到網盤,微信掃碼關注獲取網盤地址和提取碼

歡迎掃碼關注

二、運行時數據區

根據《Java虛擬機規範》的規定,Java虛擬機所管理的內存將會包括以下幾個運行時數據區域,如下圖所示:
在這裏插入圖片描述

2.1 先記憶此圖【運行時數據區】3分鐘

直到可以自己將此圖畫出爲止,在線畫圖可以藉助ProcessOn。

背誦:
運行時數據區分爲所有線程共享區域、線程隔離(也叫線程獨佔)的區域兩部分。
共享區域分爲方法區和堆(1.8中稱爲元數據,後面詳細解釋)。
線程隔離(也叫獨佔)區域,包括虛擬機棧,本地方法棧,程序計數器。(2棧1計數
運行時數據區,關聯執行引擎,本地庫接口,本地庫接口關聯本地方法庫。

2.2 運行時數據區概念理解

Java虛擬機在實行Java程序的過程中,會把它所管理的內存劃分爲若干個不同的數據區域。有些區域是隨着虛擬機進程啓動而存在,有些是依賴用戶線程來建立和銷燬。

這裏大家可以思考下,常說的垃圾回收主要是在哪片內存區域呢?

2.3 程序計數器

英文要知道Program Counter Register 。

由於Java虛擬機的多線程,是通過線程輪流切換並分配處理器執行時間的方式來實現的。一核的處理器只會執行一條線程中的指令。

那如果這次處理器分配時間太短,線程指令還未結束完,那等處理器第二次執行時,就需要知道第一次執行到哪裏纔行。

程序計數器,是一塊比較小的內存空間,可以看做是當前線程所執行的字節碼的行號指示器,保證線程切換後可以繼續執行。
**在Java虛擬機的概念模型裏,字節碼解釋器工作時,就是通過改變計數器的值來選取下一條需要執行的字節碼指令。
每一個線程都會有自己的程序計數器,大家互不干擾。所以程序計數器,是在線程獨佔(線程隔離)區域

  • 如果線程執行的是Java方法,則程序計數器記錄的是正在執行的虛擬機字節碼指令的地址;
  • 如果線程執行的是Native方法,則程序計數器值爲Undefined;
  • 程序計數器所在內存區域,是個在Java虛擬機規範中沒有規定任何OutOfMemoryError的區域。

2.4 Java虛擬機棧

Java Virtual Machine Stacks

  • 線程私有的,生命週期與線程相同。
  • 描述了Java方法執行的內存模型:每個方法被執行的時候,會創建一個棧幀,用於存儲局部變量表,操作數棧,動態鏈接,方法出口等信息。
  • 每個方法被調用直至執行完成,就對應一個棧幀在虛擬機棧從入棧到出棧的過程。
  • 之前書本上的堆棧,“棧”就是對應的Java虛擬機棧中的局部變量表部分。
  • 局部變量表存放了編譯期可知的各種Java虛擬機基本數據類型,對象引用,和returnAddress類型(指向了一條字節碼指令的地址)。這些數據類型在局部變量表中存儲空間以局部變量槽(Slot)來表示,其中64位長度的long和double類型的數據佔用2個局部變量槽,其餘數據類型佔用1個。
  • 局部變量表所需的內存空間,編譯期間完成分配,方法運行時不會改變局部變量表的大小。這裏的大小指的是槽的數量,至於每個槽佔用多少個bit,是虛擬機來決定的。
  • Java虛擬規範中,對這個區域規定了兩種異常情況:
    a、如果線程請求的棧深入大於虛擬機所允許的深度,則會拋出StackOverflowError異常。
    b、如果虛擬機棧可以動態擴展,當擴展到無法申請足夠的內存時,會拋出OutOfMemoryError異常。

注意:Java虛擬機棧也會出現OutOfMemoryError異常

2.5 本地方法棧

Native Method Stacks

  • 與虛擬機棧作用相似,虛擬機棧爲執行Java方法服務,本地方法棧爲虛擬機使用的Native方法服務。
  • 本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。

2.6 Java堆

Java Heap

  • 是Java虛擬機所管理的內存中最大的一塊,被所有線程共享的一塊內存區域。幾乎所有的對象實例都在這裏分配內存。
  • 虛擬機啓動時創建,唯一目的就是存放對象實例。
  • Java堆是垃圾回收管理的主要區域。
  • 不少資料中寫着Java虛擬機的堆內存分爲新生代,老年代,永久代,Eden,Survivor,十年前(以G1收集器的出現爲分界),HotSpot虛擬機內部的垃圾收集器全部基於“經典分代”來設計,需要新生代、老年代收集器搭配才能工作。這種說法還時不會產生太多歧義,但是到了今天,HostSpot裏也出現了不採用分代設計的垃圾收集器,再按照這個說法就很多需要商榷的地方。
  • Java堆可以是物理上不連續的內存空間,只要邏輯連續就可以。
  • -Xmx最大值 -Xms最小值

2.7 方法區

Method Area

  • 與Java堆一樣,是各個線程的共享區域。
  • 用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
  • 還有一個別名叫Non-Heap(非堆),目的是與Java堆區分開來。
  • 永久代(Permanent Generation) 並不等價於方法區,永久代是方法區實現的一種方式。僅僅是因爲HotSpot虛擬機的設計團隊,把GC分代收集擴展到了方法區,對於其他虛擬機是不存在永久代的概念的。
  • 永久代實現方法區容易導致內存溢出問題-XX:MaxPermSize上限,即使不設置也有默認值。JDK8中,完全廢棄了永久代的概念,改爲在本地內存中實現的元空間(Meta-space)來替代。
  • Java虛擬機規範,對這個區域限制非常寬鬆。不僅不需要連續的內存,還可以選擇部不實現垃圾回收。
  • 垃圾回收在這個區域很少存在,但是並不意味真的永久存在了。這個區域主要針對常量池的回收和對類型的卸載。
  • 當方法區無法滿足內存分配需求,將拋出OutOfMemoryError異常。

2.8 運行時常量池

Runtime Constant Pool

  • 運行時常量池是方法區的一部分。用於存放編譯期生成的各種字面量和符號引用。這部分內容將在類加載後存放到方法區的常量池中。
  • 字面量和符號引用等後面會詳細介紹。
  • 運行時常量池,對於Class文件常量池,最重要特徵是具備動態性,運行期間也可能將新的常量放入池中。例如String類的intern()方法,具體可以參考博客:https://blog.csdn.net/tyyking/article/details/82496901
  • 常量池無法再申請到內存時也會拋出OutOfMemoryError異常。

2.9 直接內存

Direct Memory

  • 並不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域。
  • 頻繁使用,也可能導致OutOfMemoryError異常。
  • JDK1.4新增了NIO類,引入基於通道與緩衝區的I/O方式,它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆裏面的DirectByteBuffer對象作爲這塊內存的引用進行操作。
  • 在一些場景顯著提高性能,避免了再Java堆和Native堆中來回複製數據。
  • 要避免配置虛擬機參數時,會根據實際內存設置-Xmx等參數,忽略了直接內存,使得各個區域的內存綜合大於物理內存限制和操作系統限制,從而導致動態擴展出現OOM。

三、階段總結

本篇博客名詞,理論部分比較多。
初學需要大家強化記憶,後面等學完所有的虛擬機相關知識後,這些概念纔會深入理解。

現在我們回憶一下,Java虛擬機運行時數據區有哪些組成部分?並分別用一句話重點概括:

  • Java虛擬機運行時數據區,分爲線程共享區域,線程獨佔區域。
  • 線程共享區域有方法區,和堆。(1.8稱爲元數據)
  • 線程獨佔區域有Java虛擬機棧,本地方法棧,程序計數器。
  • 執行引擎關聯本地庫接口,本地庫接口關聯本地方法庫。

博客內容大部分來自《深入學習Java虛擬機 第三版》一書。

下一篇博客,我們會介紹對象再HostSpot虛擬機中如何創建,佈局訪問的,謝謝大家。

感謝大家的閱讀,歡迎大家長按二維碼關注,私信交流,轉發,點在看,謝謝!

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