jvm入門及理解(三)——運行時數據區(程序計數器+本地方法棧)

一、內存與線程

內存:

內存是非常重要的系統資源,是硬盤和cpu的中間倉庫及橋樑,承載着操作系統和應用程序的實時運行。JVM內存布局規定了JAVA在運行過程中內存申請、分配、管理的策略,保證了JVM的高效穩定運行。不同的jvm對於內存的劃分方式和管理機制存在着部分差異(對於Hotspot主要指方法區)

 

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

如圖,灰色的區域為單獨線程私有的,紅色的為多個線程共享的,即

  • 每個線程:獨立包括程序計數器、棧、本地棧
  • 線程間共享:堆、堆外內存(方法區、永久代或元空間、代碼緩存)

一般來說,jvm優化95%是優化堆區,5%優化的是方法區。

 

線程:

 

  • 線程是一個程序里的運行單元,JVM允許一個程序有多個線程並行的執行;
  • 在HotSpot JVM,每個線程都與操作系統的本地線程直接映射。
  • 當一個java線程準備好執行以後,此時一個操作系統的本地線程也同時創建。java線程執行終止後。本地線程也會回收。
  • 操作系統負責所有線程的安排調度到任何一個可用的CPU上。一旦本地線程初始化成功,它就會調用java線程中的run()方法.

 

查看jvm線程的方法:

使用jconsole

 

HotSpot JVM後台主要線程:

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

 

二、運行時數據區概覽

Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為上圖若干個不同的數據區域。這些區域都有各自的用途,已經創建和銷毀時間,有的區域隨着虛擬機進程的啟動而創建,有些區域則依賴用戶線程的啟動和結束而創建和銷毀。

三、程序計數器(pc寄存器)

 

介紹:JVM中的程序計數寄存器(Program Counter Register)中,Register的命名源於CPU的寄存器,寄存器存儲指令相關的現場信息。CPU只有把數據裝到寄存器才能夠運行。JVM中的PC寄存器是對計算機PC寄存器的一種抽象模擬。

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

  • 它是一塊很小的內存空間,幾乎可以忽略不計。也是運行速度最快的存儲區域
  • 在jvm規範中,每個線程都有它自己的程序計數器,是線程私有的,生命周期與線程的生命周期保持一致
  • 任何時間一個線程都只有一個方法在執行,也就是所謂的當前方法。程序計數器會存儲當前線程正在執行的java方法的JVM指令地址;或者,如果實在執行native方法,則是未指定值(undefined)。
  • 它是程序控制流的指示器,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成
  • 位元組碼解釋器工作時就是通過改變這個計數器的值來選取嚇一跳需要執行的位元組碼指令
  • 它是唯一一個在java虛擬機規範中沒有規定任何OOM情況的區域

 

 

 CPU時間片

  • CPU時間片即CPU分配各各個程序的時間,每個線程被分配一個時間段。稱作它的時間片。
  • 在宏觀上:我們可以同時打開多個應用程序,每個程序並行不悖,同時運行。
  • 但在微觀上:由於只有一個CPU,一次只能處理程序要求的一部分,如何處理公平,一種方法就是引入時間片,每個程序輪流執行。
  • 並行:同一時間多個線程同時執行;
  • 並發:一個核快速切換多個線程,讓它們依次執行,看起來像並行,實際上是並發

四、本地方法棧

  • Java虛擬機棧用於管理Java方法的調用,而本地方法棧用於管理本地方法的調用
  • 本地方法棧,也是線程私有的。
  • 允許被實現成固定或者是可動態拓展的內存大小。(在內存溢出方面是相同的)
    • 如果線程請求分配的棧容量超過本地方法棧允許的最大容量,Java虛擬機將會拋出一個StackOverFlowError異常。
    • 如果本地方法棧可以動態擴展,並且在嘗試擴展的時候無法申請到足夠的內存,或者在創建新的線程時沒有足夠的內存去創建對應的本地方法棧,那麼java虛擬機將會拋出一個OutOfMemoryError異常。
  • 本地方法是使用C語言實現的
  • 它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載本地方法庫。
  • 當某個線程調用一個本地方法時,它就進入了一個全新的並且不再受虛擬機限制的世界。它和虛擬機擁有同樣的權限
    • 本地方法可以通過本地方法接口來 訪問虛擬機內部的運行時數據區
    • 它甚至可以直接使用本地處理器中的寄存器
    • 直接從本地內存的堆中分配任意數量的內存
  • 並不是所有的JVM都支持本地方法。因為Java虛擬機規範並沒有明確要求本地方法棧的使用語言、具體實現方式、數據結構等。如果JVM產品不打算支持native方法,也可以無需實現本地方法棧。
  • 在hotSpot JVM中,直接將本地方法棧和虛擬機棧合二為一。
Tags: