1. 程式人生 > >關於Java虛擬機器執行時資料區域的總結

關於Java虛擬機器執行時資料區域的總結

Java虛擬機器執行時資料區域

程式計數器(Program Counter)


程式計數器作為一個概念模型,這個是用來指示下一條需要執行的位元組碼指令在哪。

Java的多執行緒實際上是通過執行緒輪轉做到的,如果是一個單核的機器(或單cpu),嚴格意義上在一個時間塊中只會有一個執行緒在執行。為了執行緒切換以後能恢復到正確的執行位置,每個執行緒都需要有一個單獨的計數器,每個計數器之間要是獨立的互不干擾。

如果執行緒執行的是Java方法,那麼PC指向的是正在執行的虛擬機器位元組碼指令的區域,如果執行的是native方法,那麼它是undefined。

Java虛擬機器棧


Java virtue machine也是執行緒私有的,它擁有一個和執行緒相同的生命週期

虛擬機器棧描述的是Java方法執行的記憶體模型;stack frame(棧幀)是一個經常談及的概念,它用來儲存內部變量表,運算元棧,動態連結,方法出口等等。

每一個方法從呼叫到執行完畢,也就對應著一個棧幀在虛擬機器棧中的入棧和出棧

我們以前畫圖來說明記憶體區的時候,總是去關注Heap(堆記憶體)和stack(棧記憶體)這兩部分,這是與物件記憶體分配最相關的兩塊記憶體區。通常所說的stack就是虛擬機器棧,或者更具體的說是虛擬機器棧中的區域性變量表。

區域性變量表存放了編譯器可知的各種基本資料型別(boolean byte double char int short long float)物件引用(reference型別,並不是物件本身,可能是地址的引用指標,也可能是一個代代表物件的控制代碼)return address型別(指向一條位元組碼指令的地址)

區域性變量表的意義就在於,可以把表所需的記憶體在編譯器就進行分配,每次程式去呼叫一個方法的時候,方法需要在frame中分配多少的區域性記憶體空間是確定的。

兩種異常情況

如果執行緒請求的棧的深度大於虛擬機器所允許的,就是StackOverFlowError,如果是支援動態拓展的虛擬機器(大部分的現代虛擬機器都支援)依然無法申請到足夠的記憶體,就會報出OutOfMemoryError異常。

本地方法棧


本地方法棧是和Java虛擬機器棧對應的一個概念,它們的作用也是相近的,唯一的不同是,本地方法棧執行的是native方法,而Java虛擬機器棧執行的是Java方法(也就是位元組碼)服務

在Sun的HotSpot虛擬機器裡面,本地方法棧和虛擬機器棧是一個。

Java堆


堆是被所有的執行緒所共享的一塊區域,這塊記憶體區域存在的唯一目的就是存放物件例項,在虛擬機器啟動的時候就會被建立,幾乎所有的物件例項都會在這裡被分配記憶體

所有的物件例項和陣列都要在堆上分配 --《Java虛擬機器規範》

隨著JIT編譯器的發展和逃逸技術的成熟,這句話也變得不是那麼的絕對了。

GC(garbage collection)也發生在這個區域,所以有時候也被稱為GC堆

方法區


方法區和Java堆相似,是執行緒共享的一段記憶體區域,它用於儲存已經被虛擬機器載入的類資訊,常量,靜態變數,即時編譯器編譯後的程式碼。

聽起來好像和Java堆很像,Java虛擬機器標準裡面也把它視為堆的一個邏輯部分,但是它被稱作Non-Heap,目的是和Java堆區分開來。

Permanent Generation?那麼,這個方法區就是永久代嗎,並不是。只是在HotSpot虛擬機器的設計中,用永久代來實現了方法區。(在JDK1.7中,已經把原本放在永久代的字串常量池移出了)

執行時常量池(Runtime Constant Pool)

這也是方法區的一個較重要的部分,.class檔案除了有類的版本,欄位,方法,介面等描述資訊外,還有一部分是常量池,用於在存放編譯期生成的各種字面量(Literal)和符號引用(Symbolic References),這部分的內容在類載入以後進入執行時常量池中存放。

字面量比較好理解,是Java語言層面的常量,例如文字字串,宣告為final的變數

符號引用這個我第一時間沒看懂什麼意思,其實是編譯原理的一個概念,包括以下的三種常量:

  • 類和介面的全限定名
  • 欄位名稱和描述符
  • 方法名稱和描述符

動態性,這是執行時常量池的一個重要的特性,在執行期間也可以將新的常量放進常量區(包括基本包裝類和String,也可以呼叫intern()將String強制放進常量池)

為什麼需要執行時常量池呢?

  • 更少的記憶體。直接賦值的時候會利用常量池裡面的物件,而不是去new了一個

  • 更快的速度 。‘==’比equals()更快

Integer a = 23;//在編譯的時候會變成Integer i1=Integer.valueOf(40);使用的是執行緒池裡面的物件

Integer b = new Integer(23);//建立了新的物件

ps.我感覺這個的設計思路和資料庫連線池是差不多的,可以對照著去理解。

參考資料


《深入理解Java虛擬機器