日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JVM 总体概述

發(fā)布時(shí)間:2025/3/21 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM 总体概述 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)的計(jì)算機(jī),是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的。

JVM屏蔽了與具體操作系統(tǒng)平臺(tái)相關(guān)的信息,使Java程序只需生成在Java虛擬機(jī)上一次編譯,多次運(yùn)行,具有跨平臺(tái)性。JVM在執(zhí)行字節(jié)碼時(shí),實(shí)際上最終還是把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。

Java虛擬機(jī)包括一套字節(jié)碼指令集、一組寄存器、一個(gè)、一個(gè)垃圾回收堆和一個(gè)存儲(chǔ)方法區(qū)

本文將簡述以下內(nèi)容:

  • JVM是什么?

  • JVM能干什么?

  • JVM生命周期?

  • JVM組成架構(gòu)?

正文


JVM是什么

JDK、JRE和JVM對(duì)比

JVM,JRE,JDK?都是?java?語言的支柱,他們分工協(xié)作。但不同的是?Jdk?和?JRE?是真實(shí)存在的,而?JVM?是一個(gè)抽象的概念,并不真實(shí)存在。

JDK

JDK(Java Development Kit) 是?Java?語言的軟件開發(fā)工具包(SDK)。JDK?物理存在,是?programming tools、JRE?和?JVM?的一個(gè)集合。

JRE

JRE(Java Runtime Environment)Java?運(yùn)行時(shí)環(huán)境,JRE?是物理存在的,主要由Java API?和?JVM?組成,提供了用于執(zhí)行?java?應(yīng)用程序最低要求的環(huán)境。

JVM

JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)的計(jì)算機(jī)的軟件實(shí)現(xiàn),簡單的說,JVM是運(yùn)行byte code字節(jié)碼程序的一個(gè)容器。

JVM的特點(diǎn)

  • 基于堆棧的虛擬機(jī):最流行的計(jì)算機(jī)體系結(jié)構(gòu),如英特爾?X86?架構(gòu)和?ARM?架構(gòu)上運(yùn)行基于?寄存器。比如,安卓的?Davilk?虛擬機(jī)就是基于?寄存器?結(jié)構(gòu),而?JVM?是基于棧結(jié)構(gòu)的。

  • 符號(hào)引用?:除了基本類型以外的數(shù)據(jù)?(類和接口)?都是通過符號(hào)來引用,而不是通過顯式地使用內(nèi)存地址來引用。

  • 垃圾收集?:一個(gè)類的實(shí)例是由用戶程序創(chuàng)建和垃圾回收自動(dòng)銷毀

  • 網(wǎng)絡(luò)字節(jié)順序?:Java class文件用網(wǎng)絡(luò)字節(jié)碼順序來進(jìn)行存儲(chǔ),保證了小端的Intel x86架構(gòu)和大端的RISC系列的架構(gòu)之間的無關(guān)性。

JVM字節(jié)碼

JVM使用Java字節(jié)碼的方式,作為Java?用戶語言?和?機(jī)器語言?之間的中間語言。實(shí)現(xiàn)一個(gè)通用的、?機(jī)器無關(guān)的執(zhí)行平臺(tái)。

JVM能干什么

基于安全方面考慮,JVM?要求在?class?文件中使用強(qiáng)制性的語法和約束,但任意一門語言都可以轉(zhuǎn)換為被?JVM?接受的有效的?class?文件。作為一個(gè)通用的、機(jī)器無關(guān)的執(zhí)行平臺(tái),任何其他語言的實(shí)現(xiàn)者都可將?JVM當(dāng)作他的語言產(chǎn)品交付媒介。

JVM?中執(zhí)行過程如下:

  • 加載代碼

  • 驗(yàn)證代碼

  • 執(zhí)行代碼

  • 提供運(yùn)行環(huán)境

`JVM`生命周期

  • 啟動(dòng):任何一個(gè)擁有main方法的class都可以作為JVM實(shí)例運(yùn)行的起點(diǎn)。

  • 運(yùn)行:main函數(shù)為起點(diǎn),程序中的其他線程均有它啟動(dòng),包括daemon守護(hù)線程和non-daemon普通線程。daemon是JVM自己使用的線程比如GC線程,main方法的初始線程是non-daemon。

  • 消亡:所有線程終止時(shí),JVM實(shí)例結(jié)束生命。

`JVM`組成架構(gòu)

JAVA?代碼執(zhí)行過程如下:

1. 類加載器(Class Loader)

類加載器?負(fù)責(zé)加載程序中的類型(類和接口),并賦予唯一的名字予以標(biāo)識(shí)。

JDK?默認(rèn)提供的三種?ClassLoader如下:

類加載器的關(guān)系

  • Bootstrap Classloader?是在Java虛擬機(jī)啟動(dòng)后初始化的。

  • Bootstrap Classloader?負(fù)責(zé)加載?ExtClassLoader,并且將?ExtClassLoader的父加載器設(shè)置為?Bootstrap Classloader

  • Bootstrap Classloader?加載完?ExtClassLoader?后,就會(huì)加載?AppClassLoader,并且將?AppClassLoader?的父加載器指定為?ExtClassLoader。

  • 類加載器的作用

    Class Loader實(shí)現(xiàn)負(fù)責(zé)加載
    Bootstrap LoaderC++%JAVA_HOME%/jre/lib,?%JAVA_HOME%/jre/classes以及-Xbootclasspath參數(shù)指定的路徑以及中的類
    Extension ClassLoaderJava%JAVA_HOME%/jre/lib/ext,路徑下的所有classes目錄以及java.ext.dirs系統(tǒng)變量指定的路徑中類庫
    Application ClassLoaderJavaClasspath所指定的位置的類或者是jar文檔,它也是Java程序默認(rèn)的類加載器

    雙親委托機(jī)制

    Java中ClassLoader的加載采用了雙親委托機(jī)制,采用雙親委托機(jī)制加載類的時(shí)候采用如下的幾個(gè)步驟:

  • 當(dāng)前ClassLoader首先從自己已經(jīng)加載的類中查詢是否此類已經(jīng)加載,如果已經(jīng)加載則直接返回原來已經(jīng)加載的類。

  • 當(dāng)前ClassLoader的緩存中沒有找到被加載的類的時(shí)候,委托父類加載器去加載,父類加載器采用同樣的策略,首先查看自己的緩存,然后委托父類的父類去加載,一直到Bootstrap ClassLoader。

  • 當(dāng)所有的父類加載器都沒有加載的時(shí)候,再由當(dāng)前的類加載器加載,并將其放入它自己的緩存中,以便下次有加載請(qǐng)求的時(shí)候直接返回。

  • 小結(jié)?:雙親委托機(jī)制的核心思想分為兩個(gè)步驟。其一,自底向上檢查類是否已經(jīng)加載;其二,自頂向下嘗試加載類。

    `ClassLoader`隔離問題

    每個(gè)類裝載器都有一個(gè)自己的命名空間用來保存已裝載的類。當(dāng)一個(gè)類裝載器裝載一個(gè)類時(shí),它會(huì)通過保存在命名空間里的類全局限定名(Fully Qualified Class Name)進(jìn)行搜索來檢測(cè)這個(gè)類是否已經(jīng)被加載了。

    JVM?及?Dalvik?對(duì)類唯一的識(shí)別是?ClassLoader id?+?PackageName?+?ClassName,所以一個(gè)運(yùn)行程序中是有可能存在兩個(gè)包名和類名完全一致的類的。并且如果這兩個(gè)”類”不是由一個(gè)?ClassLoader?加載,是無法將一個(gè)類的示例強(qiáng)轉(zhuǎn)為另外一個(gè)類的,這就是?ClassLoader?隔離。

    雙親委托?是?ClassLoader類一致問題的一種解決方案,也是?Android?差價(jià)化開發(fā)和熱修復(fù)的基礎(chǔ)。

    類裝載器特點(diǎn)

    Java提供了動(dòng)態(tài)加載特性。在運(yùn)行時(shí)的第一次引用到一個(gè)class的時(shí)候會(huì)對(duì)它進(jìn)行裝載(Loading)?、** 鏈接(Linking)** 和 ** 初始化(Initialization) ** ,而不是在編譯時(shí)進(jìn)行。不同的JVM的實(shí)現(xiàn)不同,本文所描述的內(nèi)容均只限于Hotspot JVM。

    JVM的類裝載器負(fù)責(zé)動(dòng)態(tài)裝載,Java的類裝載器有如下幾個(gè)特點(diǎn):

    • 層級(jí)結(jié)構(gòu):Java里的類裝載器被組織成了有父子關(guān)系的層級(jí)結(jié)構(gòu)。Bootstrap類裝載器是所有裝載器的父親。

    • 代理模式: 基于層級(jí)結(jié)構(gòu),類的代理可以在裝載器之間進(jìn)行代理。當(dāng)裝載器裝載一個(gè)類時(shí),首先會(huì)檢查它在父裝載器中是否進(jìn)行了裝載。如果上層裝載器已經(jīng)裝載了這個(gè)類,這個(gè)類會(huì)被直接使用。反之,類裝載器會(huì)請(qǐng)求裝載這個(gè)類

    • 可見性限制:一個(gè)子裝載器可以查找父裝載器中的類,但是一個(gè)父裝載器不能查找子裝載器里的類。

    • 不允許卸載:類裝載器可以裝載一個(gè)類但是不可以卸載它,不過可以刪除當(dāng)前的類裝載器,然后創(chuàng)建一個(gè)新的類裝載器裝載。

    類裝載器過程

    • 加載(Loading)

      首先,根據(jù)類的全限定名找到代表這個(gè)類的Class文件,然后讀取到一個(gè)字節(jié)數(shù)組中。接著,這些字節(jié)會(huì)被解析檢驗(yàn)它們是否代表一個(gè)Class對(duì)象?并包含正確的major、minor版本信息。直接父類?的類和接口也會(huì)被加載進(jìn)來。這些操作一旦完成,類或者接口對(duì)象?就從二進(jìn)制表示中創(chuàng)建出來了。

    • 鏈接(Linking)

      鏈接是檢驗(yàn)類或接口并準(zhǔn)備類型和父類接口的過程。鏈接過程包含三步:校驗(yàn)(Verifying)準(zhǔn)備(Preparing)部分解析(Optionally resolving)

    • 驗(yàn)證

      這是類裝載中最復(fù)雜的過程,并且花費(fèi)的時(shí)間也是最長的。任務(wù)是確保導(dǎo)入類型的準(zhǔn)確性,驗(yàn)證階段做的檢查,運(yùn)行時(shí)不需要再做。雖然減慢加了載速度,但是避免了多次檢查。

    • 準(zhǔn)備

      準(zhǔn)備過程通常分配一個(gè)結(jié)構(gòu)用來存儲(chǔ)類信息,這個(gè)結(jié)構(gòu)中包含了類中定義的成員變量方法?和接口信息等。

    • 解析

      解析是可選階段,把這個(gè)類的常量池中的所有的符號(hào)引用改變成直接引用。如果不執(zhí)行,符號(hào)解析要等到字節(jié)碼指令使用這個(gè)引用時(shí)才會(huì)進(jìn)行。

    • 初始化(Initialization)

    把類中的變量初始化成合適的值。執(zhí)行靜態(tài)初始化程序,把靜態(tài)變量初始化成指定的值。

    JVM規(guī)范定義了上面的幾個(gè)任務(wù),不過它允許具體執(zhí)行的時(shí)候能夠有些靈活的變動(dòng)。

    2. 執(zhí)行引擎(Execution Engine)

    通過類裝載器裝載的,被分配到JVM的運(yùn)行時(shí)數(shù)據(jù)區(qū)的字節(jié)碼會(huì)被執(zhí)行引擎執(zhí)行。

    執(zhí)行引擎?以指令為單位讀取?Java?字節(jié)碼。它就像一個(gè)?CPU?一樣,一條一條地執(zhí)行機(jī)器指令。每個(gè)字節(jié)碼指令都由一個(gè)1字節(jié)的操作碼和附加的操作數(shù)組成。執(zhí)行引擎?取得一個(gè)操作碼,然后根據(jù)操作數(shù)來執(zhí)行任務(wù),完成后就繼續(xù)執(zhí)行下一條操作碼

    不過?Java?字節(jié)碼是用一種人類可以讀懂的語言編寫的,而不是用機(jī)器可以直接執(zhí)行的語言。因此,執(zhí)行引擎必須把字節(jié)碼轉(zhuǎn)換成可以直接被?JVM?執(zhí)行的語言。

    字節(jié)碼?可以通過以下兩種方式轉(zhuǎn)換成機(jī)器語言

    • 解釋器

      解釋器?一條一條地讀取字節(jié)碼解釋?并且?執(zhí)行?字節(jié)碼指令。因?yàn)樗粭l一條地解釋和執(zhí)行指令,所以它可以很快地解釋字節(jié)碼,但是執(zhí)行起來會(huì)比較慢。這是解釋執(zhí)行的語言的一個(gè)缺點(diǎn)。字節(jié)碼這種“語言”基本來說是解釋執(zhí)行的。

    • 即時(shí)(Just-In-Time)編譯器

      即時(shí)編譯器?被引入用來彌補(bǔ)解釋器的缺點(diǎn)。執(zhí)行引擎?首先按照?解釋執(zhí)行?的方式來執(zhí)行,然后在合適的時(shí)候,即時(shí)編譯器?把?整段字節(jié)碼?編譯成?本地代碼。然后,執(zhí)行引擎就沒有必要再去解釋執(zhí)行方法了,它可以直接通過本地代碼去執(zhí)行它。執(zhí)行本地代碼比一條一條進(jìn)行解釋執(zhí)行的速度快很多。編譯后的代碼可以執(zhí)行的很快,因?yàn)楸镜卮a是保存在緩存里的。

    Java?字節(jié)碼是解釋執(zhí)行的,但是沒有直接在?JVM?宿主執(zhí)行原生代碼快。為了提高性能,Oracle Hotspot虛擬機(jī)會(huì)找到執(zhí)行最頻繁的字節(jié)碼片段并把它們編譯成原生機(jī)器碼。編譯出的原生機(jī)器碼被存儲(chǔ)在非堆內(nèi)存的代碼緩存中。

    通過這種方法(JIT),Hotspot?虛擬機(jī)將權(quán)衡下面兩種時(shí)間消耗:將字節(jié)碼編譯成本地代碼需要的額外時(shí)間和解釋執(zhí)行字節(jié)碼消耗更多的時(shí)間。

    這里插入一下 Android 5.0 以后用的 ART 虛擬機(jī)使用的是 AOT 機(jī)制。

    Dalvik?是依靠一個(gè)?Just-In-Time (JIT)編譯器去解釋字節(jié)碼。開發(fā)者編譯后的應(yīng)用代碼需要通過一個(gè)解釋器在用戶的設(shè)備上運(yùn)行,這一機(jī)制并不高效,但讓應(yīng)用能更容易在不同硬件和架構(gòu)上運(yùn)行。ART?則完全改變了這套做法,在應(yīng)用安裝時(shí)就預(yù)編譯字節(jié)碼到機(jī)器語言,這一機(jī)制叫Ahead-Of-Time (AOT)編譯。在移除解釋代碼這一過程后,應(yīng)用程序執(zhí)行將更有效率,啟動(dòng)更快。

    參考

    周志明,深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐,機(jī)械工業(yè)出版社

    總結(jié)

    以上是生活随笔為你收集整理的JVM 总体概述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。