當前位置:才華齋>計算機>java語言>

Java執行時資料區

java語言 閱讀(7.84K)

Java具有簡單性、面向物件、分散式、健壯性、安全性、平臺立與可移植性、多執行緒、動態性等特點。下面是小編分享的Java執行時資料區,歡迎大家參考!

Java執行時資料區

JVM就是一個特殊的程序, 我們執行的java程式, 都執行在一個JVM程序中, 這個程序的作用就是載入class檔案, 並且執行class檔案中的程式碼。 當然, 從一個class檔案的載入, 到準備好可執行之前, 還有一段很長的路要走, 以後的文章會詳細介紹這個過程。 既然虛擬機器作為一個虛擬的計算機, 來執行我們的程式, 那麼在執行的過程中, 必然要有地方存放我們的程式碼(class檔案); 在執行的過程中, 總會建立很多物件, 必須有地方存放這些物件; 在執行的過程中, 還需要儲存一些執行的狀態, 比如, 將要執行哪個方法, 當前方法執行完成之後, 要返回到哪個方法等資訊, 所以, 必須有一個地方來保持執行的狀態。 上面的描述中, “地方”指的當然就是記憶體區域, 程式執行起來之後, 就是一個動態的過程, 必須合理的劃分記憶體區域, 來存放各種資料。 所以, 在本文中, 將會詳細介紹JVM的執行時資料區。

JVM體系結構和執行時資料區概述

要理解JVM的執行時資料區, 必須先要理解JVM的體系結構, 因為虛擬機器的體系結構基本上解釋了“為什麼會有這些執行時資料區” 。 在深入理解Java虛擬機器到底是什麼 這篇文章中也簡單的提到過JVM的體系機構, 這裡再詳細的講解一下。 JVM的體系結構如下:

由此可見, 執行時資料區的劃分, 是和JVM的體系結構相關的.。 本文主要介紹執行時資料區的劃分, 對體系結構不做深入的講解。 簡單概括一下, 類載入器子系統用於將class檔案載入到虛擬機器的執行時資料區中(準確的說應該是方法區) 。 可以認為執行引擎是位元組碼的執行機制, 一個執行緒可以看做是一個執行引擎的例項。 下面介紹執行時資料區:

 JVM執行時資料區

  方法區

在字面意思上, “方法區”這個詞會讓人產生誤解。因為方法區存放的不只是方法, 它存放的是型別資訊。我們在寫程式的時候, 幾乎總是在和類, 物件打交道, 我們知道根據一個類可以建立物件。 一般來說, 我們操縱的是物件, 訪問物件的屬性, 呼叫物件的方法等, 但是我們要思考這樣一個問題, 虛擬機器根據什麼資訊知道如何建立物件的呢? 當然是根據這個物件的型別資訊, 但是這個型別資訊在哪裡呢?現在我們知道是在方法區中。 那麼型別資訊是被誰載入到方法區中的呢?由上面的體系結構圖, 我們可以知道是類載入器子系統?那麼所謂的型別資訊, 都包含什麼資訊呢?這些資訊又是如何存放的呢?這裡的型別資訊, 可以籠統的認為就是我們前面講解過的一個class檔案,類載入器子系統將會提取class檔案裡面的型別資訊,並將這些型別資訊存放到方法區中。 至於方法區中如何存放一個型別資料, 是和JVM的具體實現相關的。 但是不管如何實現, 一個類的型別資訊總是會包含如下資訊:

類的全限定名

當前類的直接父類的全限定名

這個類是介面型別, 類型別, 還是列舉型別

類的訪問修飾符資訊

當前型別的超介面的全限定名

當前型別的常量池

欄位資訊

方法資訊

如果對class檔案格式比較熟悉的話, 可以看出, 這些資訊都是在class檔案中描述過的。 由於我們無法看到型別資訊具體是如何儲存的, 但是大致可以將型別資訊看做一個class檔案, 這有助於我們的理解。下面再次列出class檔案結構的表格,讀者可以對比class檔案中的內容到型別資料上, 該表中的各種資料已經在前面的部落格中詳細講解過:

型別資料中,除了這些基本資訊外, 型別資訊還包括以下兩個方面:

一個到類的ClassLoader物件的引用

一個到表示該類的Class例項物件的引用

 靜態變數儲存區

由於之前的部落格中詳細介紹過class檔案的格式, 對上面的一些基本資訊我們可能比較熟悉, 但是對這兩種資訊就比較陌生了。 其實說來也簡單,每個class都是被一個類載入器載入到方法區的, 型別資訊中的到類的ClassLoader物件的引用, 表明了當前的類是被哪個類載入器載入的, 這個資訊同時也標示了當前的型別的名稱空間。

每當一個class檔案被成功的載入到方法區中, JVM總會建立一個Class物件, 來唯一標示這個類。 這個Class物件可以看做是類載入過程的產物, 由於它描述了整個型別資訊, 而Java中的反射也是針對的型別資訊, 所以這個Class物件是反射的基石, 大多數反射API都是根據Class物件來實現的。

而靜態變數也是存在於型別資訊中, 可以這麼說, 型別資訊中, 會有專門的區域存放類的靜態變數。 與存在於物件中的例項變數不同, 靜態變數存在於型別資料中, 每個型別只有一份,所以也叫類變數。

方法區是一個相對來說比較固定的記憶體區, 因為它存放的是型別資訊, 而型別資訊在被載入到方法區中之後, 除了必要的連線和初始化, 一般不會有較大改動,一般情況下, JVM也不會解除安裝型別資訊, 所以方法區也可以稱為JVM的靜態區。 一個型別的生命週期一般就是整個程式的生命週期。 這也是為什麼要慎用靜態變數的原因所在, 因為靜態變數隨型別資訊存放在方法區中, 生命週期很長, 如果使用不當, 很容易造成記憶體洩露。 一個JVM例項中只存在一個方法區, 方法區中的所有型別資料被所有執行緒共享。

方法區是存放型別資料的, 而堆則是存放執行時產生的物件的。 和C++不同的是, Java只能在堆中存放物件, 而不能在棧上分配物件, 所有執行時產生的物件全部都存放於堆中, 包括陣列。 我們知道, 在Java中, 陣列也是物件。一個JVM例項中只有一個堆, 所有執行緒共享堆中的資料(物件) 。

Java虛擬機器支援幾種不同的建立物件的指令, 如new , anewarray等。 這些指令執行的結果就是在堆中分配記憶體, 並建立物件。 但是Java虛擬機器的指令集中並不包含任何釋放記憶體的指令, 因而我們也就不能手動釋放記憶體。 所有被建立的物件都會被一個叫做垃圾收集器(GC)的模組自動回收, 垃圾收集器有不同的實現方式, 他們以 特定的方式判斷物件是否過期, 並以特定的方式對物件進行回收, 關於垃圾收集的話題不是本文的重點, 這裡就不多說了。 我們只要知道:所有建立的物件都存在堆中, 而垃圾收集器會自動回收過期的物件, 所以,JVM的堆區是垃圾收集器的“重點管理區” 。

 Java棧

Java棧是一個執行緒的執行區域, 它儲存著一個執行緒中的方法的呼叫狀態, 也可以說, 一個Java執行緒的執行狀態, 都由一個Java棧來儲存。 在這個棧中, 每一方法對應一個棧幀, 請注意區分棧幀和棧這兩個概念。 棧指的是整個執行緒的執行棧, 棧幀是棧中的一個單位, 每個方法對應一個棧幀。 JVM會對Java棧執行兩種操作: 壓棧和出棧。 這兩種操作在執行時都是以幀(棧幀)為單位的。 當呼叫了一個新的方法, 就會壓入一個棧幀, 當一個方法呼叫完成, 就會彈出這個方法的棧幀, 回到呼叫者的棧幀。

舉例來說, 如果方法a呼叫了方法b, 而方法b中呼叫了方法c。 這個過程中的方法呼叫和返回的裝狀態是這樣的(其中圖中兩條虛線之間表示Java棧,每個方塊表示一個特定方法的棧幀)

Java棧上的所有資料都是執行緒私有的, 也就是說, 每個執行緒都會有自己的Java棧, 不會相互訪問其他Java棧中的資料。

 PC暫存器

pc暫存器用於存放一條指令的地址, 這條指令就是虛擬機器要執行的下一條指令。pc暫存器和執行緒相關聯, 每一個執行緒都有一個PC暫存器。

本地方法棧

我們知道Java可以和C/C++互調。如果當前執行緒執行的程式碼是C/C++寫的原生代碼, 那麼這些方法就在本地方法棧中執行,而不會在Java棧中執行, Java棧中只執行Java方法。