深入探究JVM之記憶體結構及字串常量池
阿新 • • 發佈:2020-07-21
# 前言
Java作為一種平臺無關性的語言,其主要依靠於Java虛擬機器——JVM,我們寫好的程式碼會被編譯成class檔案,再由JVM進行載入、解析、執行,而JVM有統一的規範,所以我們不需要像C++那樣需要程式設計師自己關注平臺,大大方便了我們的開發。另外,能夠執行在JVM上的並只有Java,只要能夠編譯生成合乎規範的class檔案的語言都是可以跑在JVM上的。而作為一名Java開發,JVM是我們必須要學習瞭解的基礎,也是通向高階及更高層次的必修課;但JVM的體系非常龐大,且術語非常多,所以初學者對此非常的頭疼。本系列文章就是筆者自己對於JVM的核心知識(記憶體結構、類載入、物件建立、垃圾回收等)以及效能調優的學習總結,另外未特別指出本系列文章都是基於HotSpot虛擬機器進行講解。
# 正文
JVM包含了非常多的知識,比較核心的有**記憶體結構**、**類載入**、**類檔案結構**、**垃圾回收**、**執行 引擎**、**效能調優**、**監控**等等這些知識,但所有的功能都是圍繞著**記憶體結構**展開的,因為我們編譯後的程式碼資訊在執行過程中都是存在於JVM自身的記憶體區域中的,並且這塊區域相當的智慧,不需要C++那樣需要我們自己手動釋放記憶體,它實現了**自動垃圾回收機制**,這也是Java廣受喜愛的原因之一。因此,學習JVM我們首先就得了解其記憶體結構,熟悉包含的東西,才能更好的學習後面的知識。
## 記憶體結構
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200718111234307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2w2MTA4MDAz,size_16,color_FFFFFF,t_70)
如上圖所示,JVM執行時資料區(即記憶體結構)整體上劃分為**執行緒私有**和**執行緒共享**區域,**執行緒私有**的區域生命週期與執行緒相同,**執行緒共享**區域則存在於整個執行期間 。而按照JVM規範細分則分為**程式計數器**、**虛擬機器棧**、**本地方法棧**、**方法區**和**堆**五大區域(直接記憶體不屬於JVM)。注意這只是規範定義需要存在的區域,具體的實現則不在規範的定義中。
### 1. 程式計數器
如其名,這個部件就是用來記錄程式執行的地址的,迴圈、跳轉、異常等等需要依靠它。為什麼它是執行緒私有的呢?以單核CPU為例,多執行緒在執行時是輪流執行的,那麼當執行緒暫停後恢復就需要程式計數器恢復到暫停前的執行位置繼續執行,所以必然是每個執行緒對應一個。由於它只需記錄一個執行地址,所以它是五大區域中唯一一個不會出現**OOM(記憶體溢位)**的區域。另外它是控制我們JAVA程式碼的執行的,在呼叫**native**方法時該計數器就沒有作用了,而是會由**作業系統的計數器**控制。
### 2. 虛擬機器棧
虛擬機器棧是方法執行的記憶體區域,每呼叫一個方法都會生成一個**棧幀**壓入棧中,當方法執行完成才會彈出棧。棧幀中又包含了**區域性變量表**、**運算元棧**、**動態連結**、**方法出口**。其中區域性變量表就是用來儲存區域性變數的(**基本型別值**和**物件的引用**),每一個位置32位,而像long/double這樣的變數則需要佔用兩個槽位;運算元棧則類似於快取,用於儲存**執行引擎**在計算時需要用到的區域性變數;動態連結這裡暫時不講,後面的章節會詳細分析;方法出口則包含**異常出口**和**正常出口**以及**返回地址**。下面來看三個方法示例分別展示**棧**和**棧幀**的執行原理。
- 入棧出棧過程
```java
public class ClassDemo1 {
public static void main(String[] args) {
new ClassDemo1().a();
}
static void a() { new ClassDemo1().b(); }
static void b() { new ClassDemo1().c(); }
static void c() {}
}
```
如上所示的方法呼叫**入棧出棧**的過程如下:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200718152743778.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2w2MTA4MDAz,size_16,color_FFFFFF,t_70)
- 棧幀執行原理
```java
public class ClassDemo2 {
public int work() {
int x = 3;
int y = 5;
int z = (x + y) * 10;
return z;
}
public static void main(String[] args) {
new ClassDemo2().work();
}
}
```
上面只是一簡單的計算程式,通過**javap -c ClassDemo2.class**命令反編譯後看看生成的位元組碼:
```java
public class cn.dark.ClassDemo {
public cn.dark.ClassDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/