天天看點

高頻面試題-請聊一下JVM的記憶體結構!

萬裡長征走出第一步。

今天我們就來看一下JVM的記憶體結構,雖然是概念性的知識;但憑借着理論知識,結合日常的開發工作,日積月累下來,對平時的程式設計影響甚廣。

我這裡是以JDK8為例,描述一下JVM的記憶體結構,如果想要了解更多更全面的知識,其實買本書看會更有效果,《深入了解Java虛拟機》。

畫了一張圖,先看一下!

高頻面試題-請聊一下JVM的記憶體結構!
整體來說,可以分為線程私有、線程共享兩種類型,下面來看一下吧!

線程私有

程式計數器(Program Counter Register)

線程執行的位元組碼行号訓示器,通過改變計數器的值來選取下一條要執行的位元組碼指令,隻為Java方法計數。

因為每個線程有自己獨立的行号,由于程式計數器所占記憶體小之又小,是以每個線程使用獨立計數器來處理是說得通的,這樣并不會造成什麼壓力,反而因為每個線程都私有一個計數器而快很多。

這也就是程式技術器是線程私有的原因。

虛拟機棧 (Stack)(特性:後進先出)

Java方法執行時的記憶體模型,包含着諸多個棧幀。

棧幀是方法運作期間中的基礎資料結構,每個棧幀中主要包含了局部變量、操作數棧、動态連接配接、傳回位址等等。

虛拟機棧中存儲了線程中執行方法的棧幀,隻有當方法執行完畢後,棧幀才會被銷毀。

這裡還有一個比較重要,而且問的比較頻繁的一個知識點:如何引起StackOverflowError、OutOfMemoryError異常,為什麼?

這個問題我們在之後的文章中會加以講解,大家也可以自行去研究一下。

本地方法棧

本地方法棧與虛拟機棧的功能類似,虛拟機棧是為Java方法提供服務,而本地方法棧是為Native方法服務。

線程共享

元空間(MetaSpace)

用于存儲被加載的類資訊、常量、靜态變量等等資料。

在JDK1.8之前,這些資料存儲于方法區,也就是我們常說的永久代(PermGen)。

不同于永久代占用Java虛拟機記憶體,元空間是直接使用本地記憶體存儲。

更換成元空間的好處很多,比如:

  1. 字元串常量池存放在永久代中的時候,容易出現記憶體溢出、性能問題。
  2. 類資訊的大小難以确定,因為永久代使用的是Java虛拟機記憶體,是以指定永久代記憶體時會有一定的難度。
  3. 在《深入了解Java虛拟機》一書中提到過,其他虛拟機(如:JRockit、IBM J9)等,都沒有永久代。這也就說明了Java虛拟機(HotSpot)無法與其他的虛拟機進行內建搭配;當使用了元空間後,這個問題就迎刃而解了。

堆 (Heap)

Java堆是所有線程共享的一塊記憶體區域,也是Java虛拟機中所管理的最大的一塊記憶體。

其中存儲着幾乎所有的對象執行個體,同時我們之後要說的垃圾回收機制,也是針對堆來說的,因為堆是垃圾收集器管理的主要區域。

如果根據垃圾回收集器的角度來看,堆還能分為新生代(Eden、From Survivor、To Survivor)、老年代。