大家好,我是KookNut39也是Tommy,在CSDN寫文,寫Java時候我是Tommy,分享一些自己認為在學習程序中比較重要的東西,致力于幫助初學者入門,希望可以幫助你進步,感興趣的歡迎關注博主,和博主一起學習Java知識,大家還可以去專欄查看之前的文章,希望未來能和大家共同探討技術,
文章目錄
- JVM概述
- JVM整體的四個部分
- 1.1 類加載器
- 1.1.1 類加載器程序
- 1.1.2類加載器的分類
- 1.1.2.1啟動類加載器(引導類加載器)
- 1.1.2.2擴展類加載器
- 1.1.2.3應用程式類加載器
- 1.1.3 雙親委派機制
- 1.1.4沙箱安全機制
- 1.1.5 類的主動使用和被動使用
- 1.2 運行時資料區
- 1.2.1 程式計數器(Program Counter Register)
- 1.2.2 Java 虛擬機堆疊(Java Virtual Machine Stacks)
- 1.2.2.1 Java虛擬機堆疊概述
- 1.2.2.2堆疊的運行原理
- 1.2.2.3 堆疊幀的內部
- 1.2.2.3.1區域變數表(Local Variables)
- 1.2.2.3.2 運算元堆疊(Operand Stack)(或運算式堆疊)
- 1.2.2.3.3 動態鏈接(Dynamic Linking) (或指向運行時常量池的方法參考)
- 1.2.2.3.4 方法回傳地址(Return Address)(或方法正常退出或者例外退出的定義)
- 1.2.2.3.5 一些附加資訊
- 1.2.3 本地方法堆疊
- 1.2.4 堆記憶體
- 1.2.5方法區
- 結語
了解JVM(Java虛擬機)首先我們必須了解VM(虛擬機)是什么,
所謂虛擬機(Virtual Machine),就是一臺虛擬的計算機,它是一款軟體,用來執行一系列虛擬計算機指令,大體上,虛擬機可以分為系統虛擬機和程式虛擬機,
我們經常見到的 VMware 就屬于系統虛擬機,它是完全對物理計算機的仿真,提供了一個可運行完整作業系統的軟體平臺,
程式虛擬機典型的代表就是 Java 虛擬機了,它專門為執行某個單個計算機程式而設計,在 Java 虛擬機中執行的指令我們稱為 Java 位元組碼指令,無論是系統虛擬機還是程式虛擬機,在上面運行的軟體都被限制于虛擬機提供的資源,Java 虛擬機是一種執行 Java 位元組碼檔案的虛擬計算機,它擁有獨立的運行機制,
Java 技術的核心就是 Java 虛擬機,因為所有的 Java 程式都運行在 Java 虛擬機內部,
JVM概述
JVM就是Java虛擬機,虛擬機就是一臺虛擬的計算機,是一款軟體,Java 虛擬機就是二進制位元組碼的運行環境,負責裝載位元組碼到其內部,解釋或編譯為對應平臺上的機器碼指令執行,每一條 Java 指令,Java 虛擬機中都有詳細定義,如怎么取運算元,怎么處理運算元,處理結果放在哪兒等,JVM是運行在作業系統上的,不與硬體直接互動,
JVM整體的四個部分
JVM整體組成可以分為4個部分:
- 類加載器(ClassLoader)
- 運行時資料區(Runtime Data Area)
- 執行引擎(Execution Engine)
- 本地方法介面(Native Interface)
程式在執行之前先要把 Java 代碼轉換成位元組碼(.class 檔案),JVM 首先需要把位元組碼通過一定的類加載器(Class Loader)把檔案加載到記憶體中運行時資料區(Runtime Data Area),而位元組碼檔案是 JVM 的一套指令集規范,并不能直接交給底層作業系統去執行,因此需要特定的命令決議器(執行引擎(Execution Engine)) 將位元組碼翻譯成底層系統指令再交由 CPU 去執行,而這個程序中需要呼叫其他語言的介面(本地方法介面(Native Interface)) 來實作,整個程式的功能,這就是這 4 個主要組成部分的職責與功能,
1.1 類加載器
Java編譯器會為虛擬機轉換源指令,虛擬機代碼存盤在以 .class 為擴展名的類檔案中,每個類檔案都包含某個類或者介面的定義和實作代碼,
? ——Java核心基礎(卷二)
1.1.1 類加載器程序
類加載器子系統負責從檔案系統或者網路中加載 class 檔案, class 檔案在檔案開頭有特定的檔案標識(位元組碼檔案都以 CA FE BA BE 標識開頭),classLoader 只負責 class 檔案的加載,至于它是否可以運行,則由執行引擎決定,加載的類資訊存放于一塊稱為方法區的記憶體空間,除了類的資訊外,方法區中還會存放運行時常量池資訊,可能還包括字串字面量和數字常量(這部分常量資訊是 class 檔案中常量池部分的記憶體映射),
類加載的程序
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XZGhPUyU-1631455118241)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1631431133481.png)]](https://img.uj5u.com/2021/09/14/264609141610531.png)
1.加載程序是把class檔案(位元組碼檔案)加載到記憶體中(I/O讀寫),類加載器把檔案加載到記憶體中,會為每個類創建一個Class類的物件,呼叫Class類中的方法獲取類的相關資訊,
2.驗證是檢驗加載的類是否有正確的內部結構并和其他類協調一致,
3.準備階段為類的靜態屬性分配記憶體,并設定默認初始值(不包含final修飾的static常量),也不會給實體變數初始化,
4.決議是將二進制資料中的符號參考替換成直接參考(符號參考是用一組符號描述所參考的目標;直接參考是指向目標的指標),
5.類初始化
? 5.1 什么時候初始化類
? 1 )創建類的實體,也就是 new 一個物件
? 2)訪問某個類或介面的靜態變數,或者對該靜態變數賦值
? 3)呼叫類的靜態方法
? 4)反射
? 5)初始化一個類的子類(會首先初始化子類的父類)
? 5.2 類的初始化順序:
? 父類static --> 子類static --> 父類構造方法 --> 子類構造方法
1.1.2類加載器的分類
1.1.2.1啟動類加載器(引導類加載器)
是由c/c++實作,用來加載Java的核心類別庫
1.1.2.2擴展類加載器
由Java實作,派生于 ClassLoader 類,上層類加載器是引導類加載器(啟動類加載器),加載底層類別庫
1.1.2.3應用程式類加載器
Java實作,派生于 ClassLoader 類,上層類加載器是擴展類加載器,加載自定義類,
1.1.3 雙親委派機制
加載類時,向上委派,交給最上層的加載器啟動類加載器加載核心類別庫,加載不到就用擴展類加載器加載底層類別庫,加載不到就用應用程式類加載器加載自定義類
雙親委派機制的優點
- 安全,可避免用戶自己撰寫的類動態替換 Java 的核心類(自定義的類名和Java核心類名相同)
- 避免全限定命名的類重復加載(使用了 findLoadClass()判斷當前類是否已加載)
1.1.4沙箱安全機制
作用是防止惡意代碼污染 Java 源代碼
如果一個類在引導類加載器那里就加載到了,先找到先使用,所以就使用引導類加載器里面的類,后面的一概不能使用,這就保證了不被惡意代碼污染,
1.1.5 類的主動使用和被動使用
JVM 規定,每個類或者介面被首次主動使用時才對其進行初始化,有主動使用,自然就有被動使用,
主動使用:
- 通過new關鍵字被導致類的初始化,這是大家經常使用的初始化一個類的方式,
他肯定會導致類的加載并且初始化 - 訪問類的靜態變數,包括讀取和更新
- 訪問類的靜態方法
- 對某個類進行反射操作,會導致類的初始化
- 初始化子類會導致父類的的初始化
- 執行該類的 main 函式
被動使用:其實除了上面的幾種主動使用其余就是被動使用了
注意:主動使用和被動使用的區別在于類是否會被初始化.
- 參考該類的靜態常量,注意是常量,不會導致初始化,但是也有意外,這里的常量是指已經指定字面量的常量,對于那些需要一些計算才能得出結果的常量就會導致初始化,比如:
public final static int number = 5 ; //不會導致類初始化,被動使用
public final static int random = new Random().nextInt() ; //會導致類的初始化,主動使用 - 構造某個類的陣列時不會導致該類的初始化,比如:
Student[] students = new Student[10] ;
1.2 運行時資料區
JVM 的運行時資料區,不同虛擬機實作可能略微有所不同,但都會遵從 Java 虛擬機規范,Java 8 虛擬機規范規定,Java 虛擬機所管理的記憶體將會包括程式計數器、Java虛擬機堆疊、本地方法堆疊、Java堆、方法區,
1.2.1 程式計數器(Program Counter Register)
JVM 中的程式計數暫存器中的 Register 命名源于CPU 的暫存器,暫存器存盤指令相關的現場資訊,這里,并非是廣義上所指的物理暫存器,或許將其翻譯為 PC 計數器(或指令計數器)會更加貼切(也稱為程式鉤子),JVM 中的PC 暫存器是對物理 PC 暫存器的一種抽象模擬,
- 它是一塊很小的記憶體空間,幾乎可以忽略不計,但是它是運行速度最快的存盤區域,
- 在 JVM 規范中,每個執行緒都有它自己的程式計數器,是執行緒私有的,生命周期與執行緒生命周期保持一致,
- 任何時間一個執行緒都只有一個方法在執行,也就是所謂的當前方法,程式計數器會存盤當前執行緒正在執行的Java方法的JVM指令地址,如果是在執行native方法,則是未指定值(undefined),
- 它是程式控制流的指示器,分支、回圈、跳轉、例外處理,執行緒恢復等基礎功能都需要依賴這個計數器來完成,
- 位元組碼解釋器作業時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,
- 它是唯一一個在Java虛擬機規范中沒有規定任何 OutOfMemoryError 情況的區域.
那么為什么使用程式計數器記錄當前執行緒的執行地址呢?我們都知道CPU需要不停的切換各個執行緒,這時候從其他執行緒切換到這個執行緒時,根據程式計數器就知道該從哪里開始執行了,JVM 的位元組碼解釋器就需要通過改變程式計數器的值來明確下一條應該執行什么樣的位元組碼指令,由于這個原因,必須為每個執行緒分配一個程式計數器,這樣各個執行緒就可以互不干擾,
1.2.2 Java 虛擬機堆疊(Java Virtual Machine Stacks)
首先我們需要學會區分堆疊和堆:
堆疊是運行時的單位,而堆時存盤的單位,
- 堆疊:解決程式的運行問題,即程式如何執行,或者說如何處理資料,
- 堆:解決的是資料存盤的問題,即資料怎么放,放在哪兒,
1.2.2.1 Java虛擬機堆疊概述
每個執行緒在創建時都會創建一個虛擬機堆疊,其內部保存一個個堆疊幀,對應著一次方法的呼叫,Java 虛擬機堆疊是執行緒私有的,生命周期和執行緒一致,堆疊中的資料都以堆疊幀為單位存盤,堆疊幀是一個記憶體區塊,是一個資料集,維系著方法執行程序中的各種資料資訊,
作用:主要負責Java程式的運行,保存方法內的區域變數,還有部分結果,還參與方法的呼叫和回傳,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6hPYfqf3-1631455118244)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1631450877953.png)]](https://img.uj5u.com/2021/09/14/264609141610532.png)
堆疊是一種快速有效的分配存盤方式,訪問速度僅次于程式計數器,JVM 直接對 Java 堆疊的操作只有兩個:
- 呼叫方法,進堆疊,
- 執行結束后出堆疊,
注意:對于堆疊來說不存在垃圾回收問題,
1.2.2.2堆疊的運行原理
- JVM 直接對 Java 堆疊的操作只有兩個,就是對堆疊幀的壓堆疊和出堆疊,遵循”先進后出“后進先出的原則,
- 在一潭訓動的執行緒中,一個時間點上,只會有一個活動堆疊,即只有當前在執行的方法的堆疊幀(此時這個堆疊楨在堆疊頂)是有效地,這個堆疊幀被稱為當前堆疊(Current Frame),與當前堆疊幀對應的方法稱為當前方法(Current Method),定義這個方法的類稱為當前類(Current Class),
- 執行引擎運行的所有位元組碼指令只針對當前堆疊幀進行操作,
- 如果在該方法中呼叫了其他方法,對應的新的堆疊幀就會被創建出來,放在堆疊的頂端,成為新的當前堆疊幀,
- 不同執行緒中所包含的堆疊幀(方法)是不允許存在相互參考的,即不可能在一個堆疊中參考另一個執行緒的堆疊幀(方法),
- 如果當前方法呼叫了其他方法,方法回傳之際,當前堆疊幀會傳回此方法的執行結果給前一個堆疊幀,接著虛擬機會丟棄當前堆疊幀,使得前一個堆疊幀重新成為當前堆疊幀,Java 方法有兩種回傳的方式,一種是正常的函式回傳,使用 return 指令,另一種是拋出例外,不管哪種方式,都會導致堆疊幀被彈出.
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KE7YAljC-1631455118245)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1631451939150.png)]](https://img.uj5u.com/2021/09/14/264609141610533.png)
1.2.2.3 堆疊幀的內部
每個堆疊幀中都有:區域變數表、運算元堆疊、動態鏈接、方法回傳地址、一下附加資訊,
1.2.2.3.1區域變數表(Local Variables)
區域變數表用于存放方法引數和方法內部定義的區域變數,
對于基本資料型別的變數,則直接存盤它的值,對于參考型別的變數,則存的是指向物件的參考,
1.2.2.3.2 運算元堆疊(Operand Stack)(或運算式堆疊)
程式中的所有計算程序都是在借助于運算元堆疊來完成的,
1.2.2.3.3 動態鏈接(Dynamic Linking) (或指向運行時常量池的方法參考)
因為在方法執行的程序中有可能需要用到類中的常量或方法,所以必須要有一個參考指向運行時常量池,
1.2.2.3.4 方法回傳地址(Return Address)(或方法正常退出或者例外退出的定義)
當一個方法執行完畢之后,要回傳之前呼叫它的地方,因此在堆疊幀中必須保存一個方法回傳地址,
1.2.2.3.5 一些附加資訊
例如和除錯相關的資訊,這部分資訊完全取決于不同的虛擬機實作,
1.2.3 本地方法堆疊
-
Java 虛擬機堆疊管理 java 方法的呼叫,而本地方法堆疊用于管理本地方法的呼叫,
-
本地方法堆疊也是執行緒私有的.
-
允許被實作成固定或者是可動態擴展的記憶體大小,
記憶體溢位方面也是相同的,
如果執行緒請求分配的堆疊容量超過本地方法堆疊允許的最大容量,會拋出StackOverflowError,
如果本地方法可以動態擴展,并在擴展時無法申請到足夠的記憶體會拋出OutOfMemoryError,
- 本地方法是用 C 語言寫的.
1.2.4 堆記憶體
堆記憶體概述:
- 一個 JVM 實體只存在一個堆記憶體,堆也是 Java 記憶體管理的核心區域,
- Java 堆區在 JVM 啟動時的時候即被創建,其空間大小也就確定了,是 JVM 管理的最大一塊記憶體空間,
- 堆記憶體的大小是可以調節,一般情況可以將起始值和最大值設定為一致,這樣會減少垃圾回收之后堆記憶體重新分配大小的次數,提高效率,
- 《Java 虛擬機規范》規定,堆可以處于物理上不連續的記憶體空間中,但邏輯上它應該被視為連續的,
- 所有的執行緒共享 Java 堆,在這里還可以劃分執行緒私有的緩沖區,
- 《Java 虛擬機規范》中對 Java 堆的描述是:所有的物件實體都應當在運行時分配在堆上,
- 在方法結束后,堆中的物件不會馬上被移除,僅僅在垃圾收集的時候才會被移除,
- 堆是 GC(Garbage Collection,垃圾收集器)執行垃圾回收的重點區域,
我們在之后在細講堆記憶體的區域劃分及垃圾回識訓制
1.2.5方法區
方法區,是一個被執行緒共享的記憶體區域,其中主要存盤加載的類位元組碼、class/method/field 等元資料、static final 常量、static 變數、編譯器編譯后的代碼等資料,另外,方法區包含了一個特殊的區域“運行時常量池”,
Java 虛擬機規范中明確說明:”盡管所有的方法區在邏輯上是屬于堆的一部分,但對于HotSpotJVM 而言,方法區還有一個別名叫做 Non-Heap(非堆),目的就是要和堆分開,所以,方法區看做是一塊獨立于 Java 堆的記憶體空間,方法區在 JVM 啟動時被創建,并且它的實際的物理記憶體空間中和 Java 堆區一樣都可以是不連續的,
方法區的大小:跟堆空間一樣,可以選擇固定大小或者可擴展,
方法區的大小決定了系統可以保存多少個類,如果系統定義了太多的類,導致方法區溢位,虛擬機同樣會拋出記憶體溢位的錯誤 :java.lang.OutOfMemoryError:Metaspace,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nuwBuYtn-1631455118248)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1631455014428.png)]](https://img.uj5u.com/2021/09/14/264609141610534.png)
結語
這次就先寫這些,文中如果存在不對的地方,歡迎各位讀者批評指正,我會在今后更新本地方法介面、執行引擎和垃圾回識訓制等相關內容,如果大家感興趣,可以關注博主,我們一起交流學習,
本次的分享到這里就結束了,碼字不易,如果喜歡,賞個點贊+評論+收藏🤞🤞🤞,感謝您的支持
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/300080.html
標籤:其他
上一篇:在m1芯片的MacBook上centos8安裝parallels tool
下一篇:青龍依賴大全
