32個問題,學習Java虛擬機的運行時數據區

學習JVM虛擬機是一個比較枯燥無味的過程,剛開始基本是看不懂學不懂,而後就是似懂非懂,最後以爲好像懂了一些,到後來又以爲仍是沒懂,反正就是懵懵懂懂,過目就忘,一問就卡住,說也說不清,其實說的就是我本身。java

我以爲在學習了相關理論知識以後,除了進行實操以外,經過提問和回答的方式,也能更好的理解所學知識,並檢驗本身是否真的理解了。算法

今天咱們要學習的是Java虛擬機的運行時數據區,包括程序計數器(Program Counter Register)、Java虛擬機棧(Java Virtual Machine Stack)、本地方法棧(Native Method Stack)、堆(Heap)、方法區(Method Area)、運行時常量池(Run-Time Constant Pool)。數組

主要是基於Java SE 8的規範(The Java Virtual Machine Specification, Java SE 8 Edition)。安全

那麼接下來,咱們正式進入問答環節。數據結構

公共問題

哪些區域是線程共享的,何時建立與銷燬?

線程共享的內存區域,包括Java堆和方法區多線程

在虛擬機啓動的時候建立,在虛擬機退出的時候銷燬。併發

哪些區域是線程私有的,何時建立與銷燬?

線程私有的內存區域,包括程序計數器、Java虛擬機棧和本地方法棧函數

在線程建立的時候建立,在線程終止的時候銷燬。學習

Java虛擬機棧、堆和方法區使用的內存要保證是連續的嗎?

都不須要。(The memory for ... does not need to be contiguous.)優化

全部區域都會拋出OutOfMemoryError?

不是。程序計數器是惟一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域

程序計數器

什麼是程序計數器?有什麼用?

程序計數器,也叫PC寄存器(Program Counter Register)。

能夠看做是當前線程所執行的字節碼的行號指示器

在虛擬機的概念模型裏,字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。(具體的虛擬機實現能夠採用更高效的方式)

程序計數器對於Java方法和本地方法有區別嗎?

有區別。

若是線程正在執行的是一個Java方法,那麼這個計數器記錄的是正在執行的虛擬機字節碼指令的地址

若是線程正在執行的是一個Native方法,那麼這個計數器值是undefined

爲何每一個線程要有一個程序計數器?

爲了線程切換後能恢復到正確的執行位置。線程間進行上下文切換時,須要先保存程序計數器,在恢復線程時繼續從上次保存的程序計數器位置繼續執行。

多個線程同時執行時,須要有多個程序計數器來分別指示下一條待執行的指令。

Java虛擬機棧和本地方法棧

二者有什麼區別?

二者做用基本同樣。

區別是虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧則爲虛擬機使用到的Native方法服務

Java虛擬機棧的做用是什麼?

用於存儲棧幀

Java虛擬機棧的做用與傳統語言(例如C語言)中的棧很是相似,用於存儲局部變量與一些還沒有計算好的結果。另外,它在方法調用和返回中也扮演了很重要的角色

Java虛擬機棧在什麼時候拋出何種異常?

若是線程請求分配的棧容量超過Java虛擬機棧所容許的最大容量,將拋出StackOverflowError異常。

若是Java虛擬機棧能夠動態擴展,當擴展時沒法申請到足夠的內存,或者建立新線程時沒有足夠的內存去建立對應的虛擬機棧,將拋出OutOfMemoryError異常。

Java虛擬機棧默認大小是多少?如何修改?

虛擬機棧默認大小爲1M(之前是256K),能夠經過-Xss參數進行設置。

棧幀是什麼,何時建立與銷燬?

棧幀是用來存儲數據和部分過程結果的數據結構(操做數棧、局部變量表),同時也用來處理動態連接(dynamic linking)、方法返回值和異常分派(dispatch exception)

棧幀隨着方法的調用而建立,隨着方法的結束而銷燬。拋出了在方法內未被捕獲的異常,也算方法結束。

棧幀裏面都裝了什麼內容?

主要是局部變量表、操做數棧和指向當前方法所屬的類的運行時常量池的引用

不一樣線程的棧幀之間能夠互相引用嗎?

不能夠。Java虛擬機棧是線程私有的,於是棧幀也是線程本地私有的數據。

如何理解當前棧幀?方法相互調用如何影響棧幀?

棧幀隨着方法的調用而建立,當前正在執行的方法稱爲當前方法,對應的棧幀就是當前棧幀

  • 當前方法調用了新的方法時,新的棧幀也會隨之建立。隨着程序的控制權移交到新的方法,新的棧幀也會成爲新的當前棧幀。
  • 當方法返回時,當前棧幀會傳回此方法的執行結果給前一個棧幀。而後,虛擬機會丟棄當前棧幀,使得前一個棧幀從新成爲當前棧幀。

局部變量表

什麼是局部變量表

局部變量表(Local Variable Table)是一組變量值存儲空間,用於存放方法參數和方法內部定義的局部變量

Java虛擬機使用局部變量表來完成方法調用時的參數傳遞

局部變量表存儲在哪裏?

每個棧幀有一個本身的局部變量表,棧幀中局部變量表的長度由編譯期決定。

局部變量表存儲於類或接口的二進制表示之中(如class文件),經過方法的code屬性保存及提供給棧幀使用。

本地方法棧是必須的嗎?

若是Java虛擬機不支持native方法,或是自己不依賴傳統棧,那麼能夠不提供本地方法棧。

若是支持本地方法棧,那這個棧通常會在線程建立的時候按線程分配。

本地方法棧在什麼時候拋出何種異常?

同上:Java虛擬機棧在什麼時候拋出何種異常?

Java堆

堆的做用

存放對象實例,幾乎全部的對象實例都是在這裏分配內存的。

全部的對象實例真的都要在堆上分配嗎?

一般狀況下,全部的對象實例,包括數組都是在堆上分配的。

但隨着JIT編譯器的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會致使一些微妙變化發生,全部的對象都分配在堆上也漸漸變得不是那麼絕對了。

堆通常會怎麼劃分使用?

如今的垃圾收集器基本都採用分代收集算法,因此Java堆通常劃分爲新生代和老年代。新生代又能夠再劃分爲Eden區、From Survivor區和To Survivor區。

爲何堆內存要進行劃分?

爲了更好的管理內存,更好更快的回收與分配內存。

因爲絕大多數對象的生命週期一般比較短,所以根據對象的生命週期劃分爲新生代和老年代。這樣設計,有利於分別採用更合適的垃圾收集算法,提升回收效率。

新生代的大部分對象生命週期較短,使用複製算法只須要移動少許對象,剩下的直接清理掉。

老年代的對象生命週期相對較長,使用標記清理或標記整理算法更加高效。

本地線程分配緩衝區TLAB能夠更安全更快地分配內存。

TLAB是什麼?

TLAB是指本地線程分配緩衝區(Thread Local Allocatoin Buffer),即給對象分配內存以前,每一個線程在Java堆中預先申請的一小塊內存

TLAB有什麼做用?

一方面是爲了解決多線程併發分配內存的線程安全問題,另外一方面能夠更快的分配內存

TLAB如何工做?

給對象分配內存以前,每一個線程在Java堆中預先申請一小塊內存(本地線程分配緩衝區)。哪一個線程要分配內存,就在哪一個線程的TLAB上分配。當TLAB使用完了,須要從新申請一塊新的內存。

方法區

什麼是方法區,以及其做用?

方法區是堆的邏輯組成部分。

方法區與傳統語言中的編譯代碼存儲區(storage area for compiled code)或者操做系統進程的正文段(text segment)的做用很是相似。

它存儲了每個類的結構信息,例如,運行時常量池、字段和方法數據、構造函數和普通方法的字節碼內容,還包括一些在類、實例、接口初始化時用到的特殊方法(實例初始化方法、類或接口的初始化方法)。

(存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。)

當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError異常。

方法區進行垃圾回收的目的是什麼?

主要是針對常量池的回收和對類型的卸載。簡單的虛擬機實現能夠選擇在這個區域不實現垃圾收集與壓縮。

方法區就是永久代嗎?

本質上二者是不等價的。

方法區是Java虛擬機運行時數據區的一個組成部分,而永久代屬於具體虛擬機的一個實現。

只是由於HotSpot虛擬機的設計者選擇把GC分代收集擴展至方法區,或者說使用永久代來實現方法區而已。

而對於其餘虛擬機來講,多是沒有永久代的概念的。

使用永久代來實現方法區有缺點嗎?

使用永久代來實現方法區,如今看來並非一個好主意,由於這樣更容易遇到內存溢出問題

在目前已經發布的JDK 1.7的HotSpot中,已經把本來放在永久代的字符串常量池移出。

JDK 1.8使用元空間取代了永久代,主要用於存儲類的元數據。

運行時常量池

運行時常量池是什麼?

是class文件中每個類或接口的常量池表的運行時表示形式

它包括了若干種不一樣的常量,從編譯期可知的數值字面量到必須在運行期解析後才能得到的方法或字段引用。

運行時常量池相似於傳統語言中的符號表(symbol table),不過它存儲的數據範圍更爲普遍。

建立類或接口時,當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError異常。

經過javap命令,能夠查看class文件的常量池信息。

什麼時候建立?

在虛擬機啓動的時候,具體在加載類和接口到虛擬機後,就建立對應的運行時常量池。

在哪裏分配?

每個運行時常量池都在Java虛擬機的方法區中分配。


閒來無聊,也開了個免費知識星球。談技術,也聊人生。


題圖:www.pexels.com

參考

《The Java Virtual Machine Specification, Java SE 8 Edition》

《Java虛擬機規範》(Java SE 8版)愛飛翔 周志明 等譯

《深刻理解 Java 虛擬機》第2版 周志明 著

Java Class文件結構實例分析

Java Class文件結構實例分析(上)

Java Class文件結構實例分析(下)

JVM指令分析實例

JVM指令分析實例一(常量、局部變量、for循環)

JVM指令分析實例二(算術運算、常量池、控制結構)

JVM指令分析實例三(方法調用、類實例)

JVM指令分析實例四(數組、switch)

JVM指令分析實例五(操做數棧)

我的公衆號

更多文章,請關注公衆號:二進制之路

二進制之路
相關文章
相關標籤/搜索