1. 程式人生 > >【JVM】程式設計師進階JVM(一)——Java記憶體區域

【JVM】程式設計師進階JVM(一)——Java記憶體區域

一、前言

      這篇部落格起,小編會向一個更加深層次、逼格滿滿的區域進發——JVM。

      可以說JVM不是一個新鮮的東西,但是做java的都會了解JVM,都聽過JVM。有的時候我們寫的程式碼執行跟JVM也有關係。

二、JVM介紹

      在java誕生的時候,就說“一處編譯,到處執行”,是什麼來保障“到處執行”的呢?

      答案就是JVM。

巨集觀把控

      要想學習jvm,就要對jvm有一個全域性把控。

      下面的圖中是小編畫的簡單的jvm的重要的內容,也是小編在後面的部落格中重點學習的,本節中,就先向大家介紹java執行時的區域。

這裡寫圖片描述

JVM的位置和作用

      我們在使用java之前呢,一開始就要安裝jdk,jdk包含了java執行環境JRE、java工具包和java基礎類庫。其中JRE又包括了JVM、Java核心類庫和支援檔案。


這裡寫圖片描述

      從上面的圖可以看出,我們寫好的java檔案,經過編譯為class檔案(位元組碼檔案)。class檔案就執行在JVM上,經過java解釋執行。

JVM發展歷史


這裡寫圖片描述

三、java記憶體區域

      針對Java的記憶體區域,我推薦大家可以用四象限的方式來學習。如下圖:

這裡寫圖片描述

      在圖中,把整個記憶體區域分層了四塊:棧、方法區、堆、程式計數器。

      有過一兩年的程式設計經驗的工程師都會聽過過這個。學過資料結構的就更不在話下。棧的最大的特點就是:“先進後出”。

      在Jvm中,棧儲存的是區域性變數、運算元棧和動態連結方法。

      2018年7月26日補充:

      棧是執行緒私有的

      棧分為了兩個部分:

  1. Java虛擬機器棧,執行緒私有。生命週期和執行緒相同。虛擬機器棧描述的是Java方法執行的記憶體模型,每個方法在執行的同時都會建立一個棧幀。每個方法從呼叫到執行完成的過程,就對應著一個棧幀在虛擬機器中入棧到出棧的過程。

  2. 本地方法棧,執行緒私有。和虛擬機器棧的區別就在於一個是為了執行Java方法服務,一個是為了虛擬機器使用到的Native方法服務。

      什麼是本地方法?帶有native的方法就是本地方法,一般是底層的方法,

這裡寫圖片描述

2018年7月26日 補充:

每個執行緒執行每個方法的時候都會在棧中申請一個棧幀,每個棧幀包括區域性變數區和運算元棧,用於存放此次方法呼叫過程中的臨時變數、引數和中間結果。

我們知道了棧中儲存是區域性變數和運算元棧等。這裡就要引出一個概念——棧幀。

現在理解為棧幀包括了局部變量表 + 運算元棧 + 動態連結 + 出口 四個部分:

  • 區域性變量表:儲存區域性變數,固定長度的資料集合,定寬的, 32位,一個int的。

  • 運算元棧:儲存sum中間結果的

  • 動態連結:有點抽象,

  • 返回地址:方法執行完成後,返回。1.正常出口,2.異常出口(上拋?,catch?)


這裡寫圖片描述 這裡寫圖片描述

      如下圖:

      a 和 b為區域性變數,初始值分別為100 和98 ,區域性變數初始化在區域性變量表中,分別位於0和1的位置,然後 int c = 0;,也是區域性變數,儲存在區域性變量表2的位置。然後是c=a+b,把a壓棧到運算元棧,把b壓棧到運算元棧,完成a+b後,把結果壓棧到運算元棧。因為c=結果,所以要把結果放入到區域性變量表的2的位置。這樣c就變成了結果。

這裡寫圖片描述

      對於更多的棧幀操作,可以參看小編的這篇部落格:

方法區

class static

      方法區主要就是儲存類、常量、靜態變數 和 即時編譯器編譯後的程式碼。

      方法區中沒有垃圾收集。

      方法區也叫做靜態區,是執行緒共享的。

      有人會問,方法區跟方法有關係嗎?

      我的回答是:沒有關係,老婆跟老婆餅有關係嗎?螞蟻跟螞蟻上樹有關係嗎?

new 的物件

      堆中主要儲存的是物件例項和陣列例項。

      堆是主要的記憶體回收區域。

一般我們把堆分為新生代和老年代。更新細緻一點的分配是Eden、From Survivor 和To Survivor。

      我們這樣分配是跟要使用的記憶體分配和回收演算法有關係,也是為了更好的分配和回收記憶體。

      堆中的資料執行緒共享

程式計數器

      主要是計數的。

      2018年7月26日補充

      可以看做當前執行緒要執行的程式碼(位元組碼)的行號指示器。位元組碼直譯器工作的時候,就是要通過改變整個計數器的值,來找下一行要執行的程式碼,無論是迴圈、選擇、順序等都要按照計數器的指示來執行下一行程式碼。

      重要的是,程式計數器是執行緒私有的,所有執行緒之間不會共享,獨立儲存。 這個是因為JVM的多執行緒是通過執行緒切換並分配處理器執行時間的方式來實現的。在任何一個確定的時刻,一個處理器(對於多核處理器來說是一個核心)都只會執行一條執行緒中的指令。因此為了執行緒切換後能恢復到正確的執行位置,每條執行緒都需要一個獨立的程式計數器,各條執行緒之間計數器互不影響,獨立儲存,所以執行緒私有。

      程式計數器是唯一一個在Java虛擬機器規範中沒有規定任何OOM的區域。

四、小結

      通過這次的學習,我們可以瞭解,我們寫的程式碼在PC中執行的時候,程式碼中的各個部分是儲存在記憶體中的什麼位置,進一步我們可以分析分析,如何寫程式碼可以讓程式執行的更快。