在本專欄的前12篇博客中, 我們主要大致介紹了什么是JVM, 并且詳細介紹了class文件的格式。 對于深入理解Java, 或者深入理解運行于JVM上的其他語言, 深入理解class文件格式都是必須的。 如果讀者對class文件的格式不是很熟悉, 在閱讀本博客下面的文章之前, 建議先讀一下前面的12篇博客, 或者參考其他資料, 熟悉class文件的格式。
在深入理解Java虛擬機到底是什么 這篇博客中, 我們有提到過, JVM就是一個特殊的進程, 我們執(zhí)行的java程序, 都運行在一個JVM進程中, 這個進程的作用就是加載class文件, 并且執(zhí)行class文件中的代碼。 當然, 從一個class文件的加載, 到準備好可執(zhí)行之前, 還有一段很長的路要走, 以后的文章會詳細介紹這個過程。 既然虛擬機作為一個虛擬的計算機, 來執(zhí)行我們的程序, 那么在執(zhí)行的過程中, 必然要有地方存放我們的代碼(class文件); 在執(zhí)行的過程中, 總會創(chuàng)建很多對象, 必須有地方存放這些對象; 在執(zhí)行的過程中, 還需要保存一些執(zhí)行的狀態(tài), 比如, 將要執(zhí)行哪個方法, 當前方法執(zhí)行完成之后, 要返回到哪個方法等信息, 所以, 必須有一個地方來保持執(zhí)行的狀態(tài)。 上面的描述中, “地方”指的當然就是內存區(qū)域, 程序運行起來之后, 就是一個動態(tài)的過程, 必須合理的劃分內存區(qū)域, 來存放各種數據。 所以, 在本文中, 將會詳細介紹JVM的運行時數據區(qū)。
要理解JVM的運行時數據區(qū), 必須先要理解JVM的體系結構, 因為虛擬機的體系結構基本上解釋了“為什么會有這些運行時數據區(qū)” 。 在深入理解Java虛擬機到底是什么 這篇文章中也簡單的提到過JVM的體系機構, 這里再詳細的講解一下。 JVM的體系結構如下:
由此可見, 運行時數據區(qū)的劃分, 是和JVM的體系結構相關的。 本文主要介紹運行時數據區(qū)的劃分, 對體系結構不做深入的講解。 簡單概括一下, 類加載器子系統(tǒng)用于將class文件加載到虛擬機的運行時數據區(qū)中(準確的說應該是方法區(qū)) 。 可以認為執(zhí)行引擎是字節(jié)碼的執(zhí)行機制, 一個線程可以看做是一個執(zhí)行引擎的實例。 下面介紹運行時數據區(qū):
類型數據中,除了這些基本信息外, 類型信息還包括以下兩個方面:
一個到類的ClassLoader對象的引用
一個到表示該類的Class實例對象的引用
靜態(tài)變量存儲區(qū)
由于之前的博客中詳細介紹過class文件的格式, 對上面的一些基本信息我們可能比較熟悉, 但是對這兩種信息就比較陌生了。 其實說來也簡單,每個class都是被一個類加載器加載到方法區(qū)的, 類型信息中的到類的ClassLoader對象的引用, 表明了當前的類是被哪個類加載器加載的, 這個信息同時也標示了當前的類型的名稱空間。
每當一個class文件被成功的加載到方法區(qū)中, JVM總會創(chuàng)建一個Class對象, 來唯一標示這個類。 這個Class對象可以看做是類加載過程的產物, 由于它描述了整個類型信息, 而Java中的反射也是針對的類型信息, 所以這個Class對象是反射的基石, 大多數反射API都是根據Class對象來實現的。
而靜態(tài)變量也是存在于類型信息中, 可以這么說, 類型信息中, 會有專門的區(qū)域存放類的靜態(tài)變量。 與存在于對象中的實例變量不同, 靜態(tài)變量存在于類型數據中, 每個類型只有一份,所以也叫類變量。
方法區(qū)是一個相對來說比較固定的內存區(qū), 因為它存放的是類型信息, 而類型信息在被加載到方法區(qū)中之后, 除了必要的連接和初始化, 一般不會有較大改動,一般情況下, JVM也不會卸載類型信息, 所以方法區(qū)也可以稱為JVM的靜態(tài)區(qū)。 一個類型的生命周期一般就是整個程序的生命周期。 這也是為什么要慎用靜態(tài)變量的原因所在, 因為靜態(tài)變量隨類型信息存放在方法區(qū)中, 生命周期很長, 如果使用不當, 很容易造成內存泄露。 一個JVM實例中只存在一個方法區(qū), 方法區(qū)中的所有類型數據被所有線程共享。