Java虛擬機——JVM(Java Virtual Machine)解析一

1.JVM是什么?

1.1 JVM概念

Java Virtual Machine (JVM) 是JDK的核心組件之一,它使得 Java 程序能夠在任何支持 JVM 的設備或操作系統上運行,而無需修改源代碼

JDK是什么,JDK和JVM是什么關系?

在這里插入圖片描述
1.Java IDE(Integrated Development Environment):集成開發環境,專門用于Java編程(例如IDEA),開發Java應用程序前需要選擇某具體版本的JDK
在這里插入圖片描述
2.JDK(Java Development Kit)
Java開發工具包,用于編寫,編譯,調試和運行Java應用程序,提供了開發Java程序所需的所有工具和資源。JDK=JRE+其他
3.JRE(Java Runtime Environment)
Java運行時環境,包含Java虛擬機(JVM),類庫和其他文件,用于運行Java程序
通過以上的內容梳理可知,JDK包含JRE,JRE包含JVM

1.2 JVM作用

說到這里,我就要搞清楚計算機是如何認識我們編寫的代碼的
首先,計算機只認識0和1,執行的指令集也是一串一串的01。所以,我們編寫的代碼一定是被轉換為二進制文件才能被計算機執行。
其次,不同的操作系統的指令集是不同的。例如,在Windows系統中0000 0010的意思是加法,而在Linux系統中的意思是減法(只是舉例,不是真正的指令集)。
那么將開發者編寫的代碼直接轉換為二進制文件,放在不同的操作系統中運行的結果可能也不同,如果要實現同樣的功能就需要在不同的操作系統上編寫不同的代碼。
Java能跨平臺運行的原因
先回憶一下剛開始學習Java編程的時候聽過的一句話"一次編譯,到處運行",這句話體現了Java的跨平臺能力,開發者只需要編寫一次Java代碼并編譯成字節碼文件,就可以在任何安裝了JVM的機器上執行。
下面是Java代碼從編寫到運行的過程
在這里插入圖片描述

java文件通過javac(Java編譯器,Java Compiler)編譯成字節碼文件(class文件)
可以把JVM看成計算機,字節碼文件就相當于JVM的指令集。然后JVM把字節碼文件轉換為對應系統的指令集,
例如:現在有"Hello World"這么一串代碼,Windows系統上的JVM將代碼轉換為0010,Linux系統上的JVM將代碼轉換為0110,最后兩個系統的執行結果都是"Hello World",這就實現了Java程序的跨平臺運行

1.3 JVM執行流程

在這里插入圖片描述
程序在執行之前先要把java代碼轉換成字節碼(class文件),JVM 首先需要把字節碼通過一定的方式類加載器(ClassLoader) 把文件加載到內存中運行時數據區(Runtime Data Area) ,而字節碼文件是 JVM 的一套指令集規范,并不能直接交個底層操作系統去執行,因此需要特定的命令解析器執行引擎(Execution Engine)將字節碼翻譯成底層系統指令再交由CPU去執行,而這個過程中需要調用其他語言的接口本地庫接口(Native Interface) 來實現整個程序的功能

2.深入學習JVM

2.1運行時數據區

運行時數據區是Java程序執行時所需的內存區域。JVM啟動時,會根據不同的內存區域分配管理內存。一般來說,線程共享的區域的生命周期和JVM一致;線程私有的區域會隨著線程的創建和銷毀跟著一起創建和銷毀,生命周期和所屬線程一致。
在這里插入圖片描述

2.1.1.方法區(線程共享)

方法區是JVM內存規范中定義的抽象概念,并不是真實的物理空間。

在JDK8以前,使用永久代來實現方法區。永久代是在堆中開辟的內存空間,主要存儲:

(1)類的元信息:類名,修飾符等
(2)常量池
(3)靜態變量
注:永久代的大小是在JVM啟動時固定的,難以根據實際需求來動態調整,而且永久代是在堆內存中開辟的空間,這也限制了永久代的大小。所以在JDK8之后使用元空間來實現方法區

在JDK8及以后,使用元空間來實現方法區(上面圖片中的元數據區/元空間不太準確,但我確實沒有找到更好的圖片)

元空間存儲方式相較于永久代有所改動。首先,元空間不再在堆內存中開辟空間,而是單獨向操作系統申請空間,這就不會再受到堆內存大小的限制。其次,元空間可以根據實際需求來動態調整大小。然后,元空間內部存儲的數據也發生了一些變化
(1)類的元信息依舊存儲在元空間中
(2)常量池轉移到堆中
(3)靜態變量
在這里插入圖片描述

2.1.2.堆(線程共享)

JVM內存中最大的部分,是所有線程共享的空間。
1.在JVM啟動時創建(可以動態調整大小,有上限),是垃圾回收的主要位置。
2.幾乎所有的對象實例(通過new創建)都存儲在堆中
3.從內存回收角度來看java堆可分為:新生代和老生代
注:JVM在編譯時會分析對象是否逃逸出方法或者線程,如果對象不會逃逸出方法或者線程,只在內部使用,JVM就可以將其分配在棧(線程私有)上,以提高性能減少垃圾回收的開銷,這里不做詳細討論

2.1.3.虛擬機棧(線程私有)

每個線程都有一個獨立的虛擬機棧,用于存儲棧幀(Stack Frame)。每個方法在執行時都會創建一個棧幀,用于存儲方法的局部變量,方法調用和返回地址

2.1.4.本地方法棧(線程私有)

存儲本地方法(Native Methods)調用的信息。本地方法是使用非Java語言(如C、C++)編寫的方法,它們通過JNI(Java Native Interface,Java本地方法接口)與Java代碼進行交互

2.1.5.程序計數器(線程私有)

存儲當前線程執行的字節碼指令的地址

2.2類加載器

2.2.1類加載過程

類加載包括:加載(Loading),連接(Linking)和初始化(Initialization)三個步驟
在這里插入圖片描述

(1)加載(Loading)

通過類加載器將硬盤中的字節碼文件加載到運行時數據區,并生成一個類對象存儲在方法區。當然,也不一定是硬盤中的字節碼文件,還可能來自于網絡、數據庫,甚至是即時生成的字節碼文件
在這里插入圖片描述
注意一:著重區分加載和類加載的區別。加載(Loading)只是類加載的第一個階段;而類加載包括加載(Loading),連接(Linking)和初始化(Initialization)三個步驟

(2)驗證(Verification)

確保類文件符合JVM規范中定義的類文件格式。
文件格式驗證:檢查文件是否是以0xCAFEBABE開頭,這是Java類文件的標識;檢查類文件的版本號是否和JVM對應,Java 8的JVM不支持Java 9的類文件
元數據驗證:確保類的元數據信息沒有語法錯誤
字節碼驗證:確保類的字節碼指令是合法的,不會導致JVM崩潰或者執行不安全操作
注意二:在今天,驗證操作不單單是驗證(Verification)這一個階段了。在解析階段還有符號引用驗證,解析階段可以發生在初始化之前,也可能發生在初始化之后(代碼中發生多態來實現后期綁定),而且JVM的開發人員還在不斷完善驗證策略,所以驗證操作分散在各個階段內,并不是單一的階段。

(3)準備(Preparation)

為類的靜態變量分配內存并設置默認值。

  1.  將類的靜態變量分配到方法區(有一些靜態變量不在方法區)
    
  2.  基本數據類型初始化默認值,int類型初始化為0,boolean類型初始化為false
    
  3.  引用類型初始化為null
    
  4.  如果是靜態常量,直接賦目標值,跳過默認值
    
(4)解析(Resolution)

將符號引用替換為直接引用。
直接引用:指向內存中的實際地址的指針或者偏移量
符號引用:是一種文本形式的引用,使用字符串或其他符號來描述目標類,字段和方法
問題:為什么要引入符號引用?
因為在class文件加載到運行時數據區之前,class文件是在硬盤或者其他空間中存儲的(反正不是內存),沒有地址和指針這個概念,如果要定位一個類,只能使用其他形式的標識符
在這里插入圖片描述
動態解析舉例:加入B類是一個抽象類,實現的是身份選擇功能,C和D類繼承了B,分別代表普通用戶和管理員。那么A到底引用C還是D,這可能需要用戶來決定。此時,A類就會先進行初始化階段,當用戶選擇完身份后再來解析

(5)初始化(Initialization)

任務:執行類的初始化代碼
觸發條件(以下任一情況都會觸發初始化):
1.創建類的實例(new
2.訪問類的靜態變量(非final)或靜態方法
3.反射調用類(Class.forName("com.example.MyClass")
4.子類初始化時,其父類會先被初始化
5. 作為程序入口的主類(包含main()方法的類)
執行順序
1.父類靜態變量和靜態代碼塊(按代碼順序執行)
2.子類靜態變量和靜態代碼塊(按代碼順序執行)
3.父類實例變量和構造代碼塊
4.父類構造函數
5.子類實例變量和構造代碼塊
6.子類構造函數

2.2.2類加載器

在上述類加載過程中,第一個階段"加載"涉及到JVM中一個非常重要的模塊——類加載器。類加載器主要負責根據類的全限定名找到對應的.class文件
在這里插入圖片描述

什么是全限定名?
全限定名指的是包含**包名**在內的**類**的完整名稱。例如,假設有一個ArrayList類,屬于java.util包,那么它的全限定名就是java.util.ArrayList
類加載器的搜索范圍:不同的類加載器負責不同路徑的類加載。在JVM中,不算自定義的類加載器,默認的類加載器有三種:

(1) 啟動/引導類加載器(Bootstrap ClassLoader):加載 `JAVA_HOME/lib` 下的核心類庫
(如 `rt.jar`)

注:這里的JAVA_HOME一般指的是JDK的安裝路徑,如下圖
在這里插入圖片描述

rt.jar(以JDK8為例):包含Java標準庫,如java.lang,java.util等,至于標準庫有哪些在Java語言規范中有明確規定,這里不過多贅述。這些標準庫中的類由啟動/引導類加載器負責加載。

(2) 擴展類加載器(Extension ClassLoader):加載 `JAVA_HOME/lib/ext` 下的擴展類

Java語言規范中沒有的類,并且是由JVM開發者添加的類,稱為擴展類,這些類由擴展類加載器負責加載。JVM的版本有很多,所以擴展類有哪些和JVM的具體版本有關

(3) 應用類加載器(Application ClassLoader):加載用戶類路徑(ClassPath)下的類

一般包括開發者編寫的類和第三方依賴庫

2.2.3 雙親委派機制(不考慮自定義類加載器)

核心思想:當類加載器收到類加載請求時,不會自行立即加載,而是先將該加載請求委派給父類加載器,最終請求會到達頂層類加載器。

完整過程:

(1)頂層加載器(啟動類加載器,Bootstrap ClassLoader)檢查JAVA_HOME/lib路徑下的核心類庫,如果能找到就加載
(2)如果啟動類加載器找不到,請求返回給擴展類加載器,檢查JAVA_HOME/lib/ext路徑下的擴展類,如果能找到就加載
(3)如果擴展類加載器找不到,請求返回給應用類加載器,檢查用戶類路徑下的類
(4)如果所有類加載器均無法加載請求類,則拋出ClassNotFoundException

雙親委派機制的優勢:

1. 避免核心類被篡改
安全性:通過優先由啟動類加載器加載核心類(如 java.lang.String),確保用戶無法定義同名類覆蓋核心類
示例:若用戶自定義 java.lang.String,JVM 會直接加載核心庫中的版本,用戶類被忽略
2.防止重復加載
唯一性:每個類由父類優先加載,確保同一個類在多個類加載器中只加載一次
示例:若父類已加載 com.example.MyClass,子類不會再重復加載,避免內存浪費和類沖突
3.天然的類隔離性
隔離性:不同類加載器加載的類屬于不同的命名空間,天然隔離。
4.靈活擴展
可定制性:允許子類加載器擴展加載范圍(如從網絡、數據庫加載類),同時不破壞核心類的穩定性

3.小結

下篇博文將繼續介紹JVM剩下核心機制——垃圾回收

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900527.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900527.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900527.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

初識 Three.js:開啟你的 Web 3D 世界 ?

3D 技術已經不再是游戲引擎的專屬,隨著瀏覽器技術的發展,我們完全可以在網頁上實現令人驚艷的 3D 效果。而 Three.js,作為 WebGL 的封裝庫,讓 Web 3D 的大門向更多開發者敞開了。 這是我開啟這個 Three.js 專欄的第一篇文章&…

OpenGL ES -> SurfaceView + EGL實現立方體紋理貼圖+透視效果

XML文件 <?xml version"1.0" encoding"utf-8"?> <com.example.myapplication.MySurfaceView xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"…

pikachu靶場搭建教程,csfr實操

靶場安裝 靶場下載地址 百度網盤下載地址和密碼 百度網盤 請輸入提取碼 0278 github靶場下載地址 https://gitcode.com/Resource-Bundle-Collection/c7cc1 安裝前提 這兩個文件夾的配置文件都要進行更改修改數據庫密碼 D:\phpstudy_pro\WWW\pikachu\inc D:\phpstudy_pro…

浙江大學DeepSeek系列專題線上公開課第二季第四期即將上線!端云協同:讓AI更懂你的小心思! - 張圣宇 研究員

今晚8點10分左右&#xff0c;端云協同&#xff1a;讓AI更懂你的小心思&#xff01;浙大學者張圣宇研究員將揭秘人機交互新玩法。浙江大學DeepSeek系列專題線上公開課第二季第四期即將上線&#xff01; 講座 主題&#xff1a; 大小模型端云協同賦能人機交互 主講人&#xff1a…

Vue3實戰三、Axios封裝結合mock數據、Vite跨域及環境變量配置

目錄 Axios封裝、調用mock接口、Vite跨域及環境變量配置封裝Axios對象調用mock接口數據第一步、安裝axios&#xff0c;處理一部請求第二步、創建request.ts文件第三步、本地模擬mock數據接口第四步、測試axiosmock接口是否可以調用第五步、自行擴展 axios 返回的數據類型 axios…

Linux如何刪除文件名包含無效編碼字符文件

在Linux中&#xff0c;文件名包含無效編碼字符或特殊不可見字符時&#xff0c;可能導致此文件無法通過常規方式選中或刪除&#xff0c;可以通過下面方法處理 1、確認文件名問題 檢查終端編碼環境 echo $LANG # 默認應為 UTF-8&#xff08;如 en_US.UTF-8&#xff09; 查看…

Completablefuture的底層原理是什么

參考面試回答&#xff1a; 個人理解 CompletableFuture 是 Java 8 引入的一個類、它可以讓我們在多線程環境中更加容易地處理異步任務。CompletableFuture 的底層原理是基于一個名為 FutureTask 的機制、結合了 監聽器模式 和 等待-通知機制 來處理異步計算。 1.首先就是Com…

C/C++ 調用約定:深入理解棧與平棧

前言 在編程中&#xff0c;理解函數調用約定和棧的機制對于編寫高效代碼、調試程序以及進行逆向工程至關重要。本文將深入探討 C 和 C 的調用約定&#xff0c;以及棧與平棧的相關知識。 C 調用約定 在 C 語言中&#xff0c;默認的調用約定是 cdecl。cdecl 調用約定的特點如下&…

xv6-labs-2024 lab1

lab-1 注&#xff1a;實驗環境在我的匯編隨手記的末尾部分有搭建教程。 0.前置 第零章 xv6為我們提供了多種系統調用&#xff0c;其中&#xff0c;exec將從某個文件里讀取內存鏡像(這確實是一個好的說法)&#xff0c;并且將其替換到調用它的內存空間&#xff0c;也就是這個…

屬性修改器 (AttributeModifier)

主頁面設置組件 import { MyButtonModifier } from ../datastore/MyButtonModifier;Entry ComponentV2 struct MainPage {// 支持用狀態裝飾器修飾&#xff0c;行為和普通的對象一致Local modifier: MyButtonModifier new MyButtonModifier();build() {Column() {Button(&quo…

【 <二> 丹方改良:Spring 時代的 JavaWeb】之 Spring Boot 中的監控:使用 Actuator 實現健康檢查

<前文回顧> 點擊此處查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、引子&…

類和對象(下篇)(詳解)

【本節目標】 1. 再談構造函數 2. Static成員 3. 友元 4. 內部類 5. 再次理解封裝 1. 再談構造函數 1.1 構造函數體賦值 在創建對象時&#xff0c;編譯器通過調用構造函數&#xff0c;給對象中各個成員變量一個合適的初始值。 #include <iostream> using name…

高精度算法

高精度加法 輸入兩個數&#xff0c;輸出他們的和&#xff08;高精度&#xff09; 輸入樣例 111111111111111111111111111111 222222222222222222222222222222 輸出樣例 333333333333333333333333333333 #include <bits/stdc.h> using namespace std;string a,b; in…

Linux開發中注意哪些操作系統安全

在 Linux 開發中&#xff0c;確保操作系統的安全至關重要。以下是一些需要注意的方面&#xff1a; 用戶管理與權限控制 合理設置用戶權限&#xff1a;為不同的用戶和用戶組分配適當的權限&#xff0c;遵循最小權限原則。避免給普通用戶過多的權限&#xff0c;以免他們誤操作或…

x64dbg調試python解釋器

可以先寫個input()這會讓dbg中斷在ntdll模塊中&#xff0c;查看調用堆棧在系統調用結束后的打斷點 然后直接斷到PyObject_Vectorcall函數

? Ultralytics YOLO驗證(Val)時自動輸出COCO指標(AP):2025最新配置與代碼詳解 (小白友好 + B站視頻)

? YOLO獲取COCO指標(3)&#xff1a;驗證(Val) 啟用 COCO API 評估&#xff08;自動輸出AP指標&#xff09;| 發論文必看&#xff01; | Ultralytics | 小白友好 文章目錄 一、問題定位二、原理分析三、解決方案與實踐案例步驟 1: 觸發 COCO JSON 保存步驟 2: 確保 self.is_coc…

【嵌入式學習3】基于python的tcp客戶端、服務器

目錄 1、tcp客戶端 2、tcp服務器 3、服務器多次連接客戶端、多次接收信息 1、tcp客戶端 """ tcp:客戶端 1. 導入socket模塊 2. 創建socket套接字 3. 建立tcp連接(和服務端建立連接) 4. 開始發送數據(到服務端) 5. 關閉套接字 """ import soc…

Linux: network: 兩臺直連的主機業務不通

前提環境,有一個產品的設定是兩個主機之間必須是拿網線直連。但是設備管理者可能誤將設置配錯,不是直連。 最近遇到一個問題,說一個主機發的包,沒有到對端,一開始懷疑設定的bond設備的問題,檢查了bond的設置狀態,發現沒有問題,就感覺非常的奇怪。后來就開始懷疑兩個主機…

COMSOL固體力學接觸

目錄 一、接觸非線性及接觸面積計算 一、接觸非線性及接觸面積計算 COMSOL接觸非線性及接觸面積計算_嗶哩嗶哩_bilibili 形成聯合體&#xff0c;在定義處右鍵選擇“建立接觸對” 位移dz使用參數化掃描。 接觸選擇定義中設置的接觸對&#xff0c;選擇罰函數&#xff0c;摩擦設置…

22.OpenCV輪廓匹配原理介紹與使用

OpenCV輪廓匹配原理介紹與使用 1. 輪廓匹配的基本概念 輪廓匹配&#xff08;Contour Matching&#xff09;是計算機視覺中的一種重要方法&#xff0c;主要用于比較兩個輪廓的相似性。它廣泛應用于目標識別、形狀分析、手勢識別等領域。 在 OpenCV 中&#xff0c;輪廓匹配主要…