三、JVM系列(Java虛擬機內存結構)

前言

在之前我們知道,Java源文件通過JavaC編譯器編譯成了字節碼文件(.class文件),接下來我們就要讓Java虛擬機運行字節碼文件,從而得到我們最終想要的結果。在這個過程,Java虛擬機會載入字節碼文件,將其存入Java虛擬機的內存空間中。
那麼字節碼數據在Java虛擬機中是如何存放的呢?Java虛擬機在爲類實例、成員變量分配內存是如何分配的呢?帶着這些問題,我們先來了解一下Java虛擬機的內存結構。

Java虛擬機內存結構(Java虛擬機規範中也稱之爲運行時數據區)

Java虛擬機內存結構可以分爲線程公有和線程私有的兩部分:

  • 公有部分
    公有部分指的是所有線程都共享的部分,包含了Java堆、方法區、常量池。
  • 私有部分
    私有部分指的是每個線程私有的數據,包含了PC寄存器、Java虛擬機棧、本地方法棧。

1、公有部分:Java堆、方法區、常量池

  • Java堆是從JVM劃分出來的一塊區域,這塊區域專門用於Java實例對象的分配,幾乎所有的對象都會在這裏進行內存分配,也就是說幾乎所有的實例對象都存儲在Java堆中。(有些時候小對象會直接在棧上進行分配,暫不介紹)
  • 方法區是存儲Java類字節碼數據的一塊區域,它存儲了每一個類的結構信息,比如運行時常量池、字段、方法數據、構造方法等。這裏我們知道常量池是存儲在方法區中的。
    Java堆我們知道是用來存儲實例對象的,並且Java堆根據對象存活時間的不同還分爲了年輕代、老年代兩個區域。而年輕代還被進一步劃分成了Eden區、From Survivor0區、To Survivor1區。如圖:



    當有對象需要內存分配的時候,優先分配到了年輕代的Eden區。等到Eden區內存不足的時候,Java虛擬機會啓動垃圾回收,此時Eden區沒有被引用的對象佔用的內存就會被回收。而一些存活比較久的對象則會進入到老年代。在Java虛擬機中有一個名爲-XX:MaxTenuringThreshold 的參數專門用來統計一個對象從年輕代進入到老年代需要GC的次數。即年輕代對象GC指定次數後,下一次GC就會進入到老年代。
    爲什麼Java堆要這樣劃分區域呢?
    在虛擬中中必然有存活時間短的對象,也有存活時間較長的對象。如果我們將其混在一起,那麼因爲存活較短的對象有很多勢必會導致頻繁垃圾回收。而垃圾回收不得不對所有的內存都進行掃描,那麼其實有一部分對象存活時間較長,對他們進行掃描完全是浪費時間。因此爲了提高回收的效率,就進行了分區。

2、私有部分:PC寄存器、Java 虛擬機棧、本地方法棧

以上我們知道Java堆和方法區方法區是線程公有的內存部分。而Java虛擬機內存中也有一部分是線程私有的。他們包括了:PC寄存器、Java虛擬機棧、本地方法棧。

  • PC寄存器
    指的是保存線程當前執行的方法。任意時刻,一條Java虛擬機線程只會執行一個方法的代碼。而這個被線程執行的方法稱爲該線程的當前方法,其地址存在PC寄存器中。
  • Java虛擬機棧
    這個棧與線程同時創建。用來存儲棧幀,即存儲局部變量、與一些過程結果的地方。
  • 本地方法棧
    當Java虛擬機使用其他語言(C語言)來實現指令集解釋器的時候,會使用帶本地方法棧(用的比較少)

小結

學到這裏我們就知道一個Java文件經過JavaC編譯器編譯成了字節碼文件,然後字節碼載入Java虛擬機內存。我們的類的信息就會存在方法區中。如果創建對象,那麼我們的對象就會存在Java堆中。如果調用方法,就會用到PC寄存器、Java虛擬機棧、本地方法棧。面對如此多的Java類,Jvm是如何決定這些類的加載順序呢?又是如何控制他們的加載呢?下一節我們講講JVM的類加載機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章