Java虛擬機器從字面意思直接理解就是執行Java的虛擬機器器,既然是虛擬的,那麼就是從物理層面來說是不存在於實際的一個機器,它不像電腦這種機器,是實際存在的,而是人們想象的一個機器,因爲它能像機器一樣做機器可以做的事情。
我們都知道Java語言是一種高階語言,我們可以從Java語言的特性中進行分析,Java特性包括物件導向、平臺獨立性、可移植性、支援多執行緒等。從列出的特性中我們可以從可移植性進行分析。
可移植性可以理解爲Java程式碼可以在在一臺機器上執行,也可以將程式碼複製到其它機器上執行,最終程式碼執行結果相同。而保證這個可移植性就要保證Java語言在不同機器上的各個型別是相同的,比如在32位元機器上御64位元機器上不會對Java程式碼造成影響。
C語言在32位元和64位元機器上型別長度是不同的,例如long型別在32位元機器中長度爲4,在64位元機器中長度爲8;但是Java語言不會存在這種問題。在32位元和64位元機器中,型別長度都是相同的。
而爲了保證Java語言中型別的不變性,就將Java語言執行在Java虛擬機器中,通過Java虛擬機器編譯Java程式碼,識別Java語言中的各個型別,最終再轉換爲機器可以識別的語言。所以Java虛擬機器的作用主要是編輯Java程式碼,將編譯好的Java程式碼轉換爲機器可識別的語言等。
所以本質上Java虛擬機器就是一套編寫好的程式碼,這個程式碼處於執行狀態時可以編譯Java程式碼,實現像機器一樣的功能。
我們現在最熟悉的Java虛擬機器應該是HotSpot VM,這是JDK1.3之後預設的虛擬機器。現在通過檢視Java虛擬機器模型也可以看到該預設的虛擬機器。
通過檢視Java VisualVM可以看到預設的JVM就是HotSpot VM。
一下會通過JVM來代替Java虛擬機器。
說完了概念再來說一下JVM的記憶體區域,既然JVM也是一套程式碼,那麼這套程式碼也有自己的架構,通過架構我們纔可以分析JVM的各個點是做什麼的!
記憶體區域圖如下:
堆是JVM的記憶體區域中佔據容量最大的一塊區域,它是執行緒共用的一塊區域,主要存放的數據爲物件。並且平時我們建立的物件都是首先在堆上進行分配空間的,一個物件佔用多少記憶體都是在首先在堆中來進行分配的。
並且堆也是垃圾回收的主要區域,當分配的物件生命週期結束,那麼就需要通過垃圾回收器來將這個物件進行回收,避免一直佔用堆中的記憶體。
若物件一直不回收,堆的可用容量達到的閾值,那麼就會拋出OutOfMemoryError異常。告訴開發者堆中的記憶體已經使用完,沒有多餘的記憶體可以給物件分配了,同時也無法再擴充套件了。
方法區也是一塊記憶體共用的區域,該區域中主要存放的數據爲被虛擬機器載入的類資訊、常數、靜態變數。平時我們建立的類、或者類中設定的常數或者靜態變數,這些數據都是存放在方法區中的。
並且方法區也是需要垃圾回收器回收的第二塊區域,垃圾回收器也會回收掉不用的常數或者靜態變數這些數據。
同時方法區也會有設定的閾值,當方法區記憶體已滿,並且不可再擴充套件時,也會拋出OutOfMemoryError的異常。
執行時常數池是方法區的一部分,該區域主要用於存放編譯器生成的各種字面量以及符號的參照。編譯器和執行期時都可以將常數放入常數池中,並且常數池的記憶體是有限的,所以使用完並且無法擴充套件時將會拋出OutOfMemoryError的異常。
虛擬機器棧是一塊執行緒私有的記憶體區域,在該區域中主要存放的數據爲區域性變數表(方法參數以及方法內部定義的區域性變數)、操作棧、方法返回地址等。
每個執行的方法都會對應一個棧幀,這個幀棧中就存放着執行方法的所有數據,例如參數、方法中的區域性變數、返回地址等資訊,也就是一個執行的方法都會在虛擬機器棧中生成一個幀棧。
該區域對應的是正在執行的方法,因此當執行緒請求的棧深度大於虛擬機器所允許的深度,那麼就會拋出StackOverflowError的異常。
同時虛擬機器棧也是有一定的閾值的,無法動態擴充套件,那麼當記憶體已經滿時,並且無法擴充套件時就會拋出OutOfMemoryError的異常。
本地方法棧也是一塊執行緒私有的記憶體區域,該區域中主要存放的數據爲虛擬機器用到的native方法服務,例如我們所使用的UnSafe類,這個類中有compareAndSwap方法,這個方法就是native方法,這些native方法服務就是存放在本地方法棧中。
該區域中也會像虛擬機器棧一樣拋出StackOverflowError和OutOfMemoryError異常。
程式計數器也是執行緒私有的,並且從字面意思可以理解到該區域主要功能爲計數,它也的確是一個起到一個計數的功能,程式計數器主要記錄的是執行緒執行的位元組碼的行號,也就是可以記錄程式碼執行到哪一行。
該區域中不會拋出異常資訊。