亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JVM工作原理和工作流程簡述

 更新時間:2020年07月13日 11:38:25   作者:Imadone  
這篇文章主要介紹了關(guān)于JVM工作原理簡述,主要弄清楚jvm運行的來龍去脈,感興趣的可以一起來了解一下

JAVA之所以跨平臺,是因為有JVM這么一個編譯和運行機器,它令對于系統(tǒng)的操作對于用戶而言是黑盒的,使得開發(fā)人員更快速和更注重軟件功能的實現(xiàn)。然而,也因為jvm是黑盒,所以內(nèi)部和底層具有不確定性,如果用狀態(tài)機來表示jvm,那么jvm就是一種現(xiàn)役復(fù)制不確定的狀態(tài)機,因為它的狀態(tài)和表現(xiàn)跟系統(tǒng)、底層、硬件等等都有關(guān)系,從而狀態(tài)是不確定,如果在分布式應(yīng)用中,jvm一直以來兼容性都不是很好,這就是主要原因。盡管如此,就單一的系統(tǒng)而言,弄清楚jvm運行的來龍去脈,對于系統(tǒng)的運行至關(guān)重要。

理解jvm的運行原理具有以下幾點充分作用:

1、針對系統(tǒng)進行內(nèi)存和垃圾回收監(jiān)控

2、解決因內(nèi)存溢出和泄露的問題

3、對系統(tǒng)進行優(yōu)化

4、提升jvm和系統(tǒng)性能

jvm的運行原理有主要有三方面,其實這也是jvm的主要工作:

1、內(nèi)存管理

2、執(zhí)行流程

3、垃圾回收

在開始之前,有一些知識需要知道,廣義來講,jvm并不是指sun的hotspot,而是一個規(guī)范,因此不同廠商會根據(jù)規(guī)范實現(xiàn)不同的jvm,因此這些jvm的表現(xiàn)都不是一致甚至相差甚遠(yuǎn)。在jvm規(guī)范中,通常我們所能接觸的就是命令行參數(shù)了。

命令行參數(shù)

命令行參數(shù)分為三種,標(biāo)準(zhǔn)、非標(biāo)準(zhǔn)、非穩(wěn)定

標(biāo)準(zhǔn)命令行參數(shù)會在jvm規(guī)范中明確列出,強制實現(xiàn)的選項,并且具有版本控制廢棄的管理通知。非標(biāo)準(zhǔn)的命令行參數(shù)不是規(guī)范強制并且可能沒有對應(yīng)的通知,非穩(wěn)定參數(shù)是特定調(diào)校的選項,同時也是非標(biāo)準(zhǔn)的。標(biāo)準(zhǔn)的選項可通過help命令查看,非標(biāo)準(zhǔn)的選項通過-X為前綴訪問,非穩(wěn)定的前綴是-XX,通常對于布爾類型的選項,用+或-來設(shè)置true或者false,如-XX:+UseTLAB開啟線程內(nèi)存緩沖分配。

內(nèi)存劃分

jvm是具有內(nèi)存自動分配和管理的架構(gòu),而內(nèi)存管理自動化是解放勞動力的重要工具,可以對比C/C++,開發(fā)人員不需要管理內(nèi)存,開發(fā)效率會比較高。

在jvm中,使用的內(nèi)存分為兩類,線程共享內(nèi)存和線程私有內(nèi)存。

結(jié)合我們平時的代碼可以看出,線程共享的內(nèi)容包括方法、實例對象、常量,分別對應(yīng)共享內(nèi)存中的方法區(qū)、堆區(qū)、常量池。

堆區(qū)

堆區(qū)通常是共享內(nèi)存中最大的一塊,因此它也是GC重點關(guān)注區(qū)域。堆區(qū)可能是連續(xù)的也可能是不連續(xù)的,以及堆區(qū)的大小都會對GC造成相應(yīng)的影響。-Xms和-Xmx設(shè)置堆的最小和最大值,如果堆內(nèi)存大小超過最大值,則拋出OutOfMemoryError異常。

方法區(qū)

方法區(qū)存儲的是方法、類的結(jié)構(gòu)信息,而常量池也包含在內(nèi),除了我們代碼中所看到的靜態(tài)常量,這些常量還包括一些字節(jié)碼內(nèi)容和類初始化所需的特殊內(nèi)容。通常情況下,jvm不會對方法區(qū)GC直到方法區(qū)大小不夠,即使GC也只是針對常量池和類型,所以也被稱為永久區(qū)Permanent Generation,除了可以設(shè)置大小以外,還可以設(shè)置是否進行GC,如果超過大小,拋出OutOfMemoryError異常。

常量池

這里說的常量池是運行時的,通常是字節(jié)碼中的類的版本、描述,以及常量池表,這個表是一種符號表,在運行的時候?qū)⑦@些符號的引用變?yōu)橹苯臃?。由此可以看出,加載類會使用常量池和方法區(qū),如果類過多或常量過多,也會拋出OutOfMemoryError異常。

線程私有內(nèi)存區(qū)

線程私有內(nèi)存是被某一獨立線程獨占的,包括PC寄存器、java棧、本地方法棧

PC寄存器

這個寄存器是jvm內(nèi)部的,而非物理寄存器,因此也可以看出,jvm的指令執(zhí)行是基于棧架構(gòu)的,所有的操作都是經(jīng)過入棧出棧完成,為了確保線程安全,它被設(shè)定為線程私有的。通常,棧中存儲字節(jié)碼指令地址,如果調(diào)用的是本地方法,即native方法,則是空值。會不會拋出OutOfMemoryError異常,jvm目前沒有明確規(guī)定。

java棧

java棧的顆粒度比PC寄存器大,存儲方法的局部變量、操作計數(shù)、方法返回\方法出口等信息。局部變量除了我們代碼所接觸的類型,還包括一種叫做returnAddress返回地址類型,也是一種jvm規(guī)范的原始類型,但是開發(fā)人員并不能使用,實際上這種類型標(biāo)識一條字節(jié)碼指令的操作嗎。java棧也會OutOfMemoryError異常,不過他也是可以動態(tài)擴展的。

本地方法棧

用于支持本地方法調(diào)用時使用,但是jvm沒有強制實現(xiàn),和java棧類似。

執(zhí)行流程

我們的代碼在IDE中或者通過CMD來執(zhí)行即可看到執(zhí)行結(jié)果,而實際上每次執(zhí)行都會啟動和關(guān)閉jvm,這個過程是相當(dāng)復(fù)雜的,下面羅列一下主要步驟。

對于sun的hotspot,launcher負(fù)責(zé)維護jvm的生命周期,包括啟動和結(jié)束關(guān)閉。就是我們在java目錄下看到的java.exe和javaw.exe,一個有控制臺輸出,另一個沒有,用于執(zhí)行GUI程序。

jvm的啟動初始化

1、解析命令行參數(shù),設(shè)置內(nèi)存大小和JIT編譯器,并且加載系統(tǒng)環(huán)境變量。

2、查找主類,并且調(diào)用本地方法JNI_CreateJavaVM創(chuàng)建jvm主線程。

3、當(dāng)jvm初始化完成,就會加載主類,如果加載成功,則調(diào)用本地方法傳入?yún)?shù),然后開始執(zhí)行java的程序了。

其中調(diào)用本地方法JNI_CreateJavaVM創(chuàng)建jvm主線程,是jvm的啟動過程,實際上啟動器并非直接調(diào)用該本地方法,而是先用main()函數(shù)創(chuàng)建主線程,然后通過主線程調(diào)用javamain()函數(shù)調(diào)用該JNI_CreateJavaVM方法創(chuàng)建子線程來完成初始化并執(zhí)行java程序。因為創(chuàng)建的主線程是操作系統(tǒng)分配的初始線程,為了更好的定制線程,通過在該線程上創(chuàng)建再初始線程來初始化jvm。

進一步細(xì)化JNI_CreateJavaVM函數(shù)的執(zhí)行內(nèi)容,主要流程如下:

1、檢查是否線程安全,也就是是否只有一個線程調(diào)用此方法,一個線程只能創(chuàng)建一個jvm實例。

2、初始化各個模塊,如日志、計數(shù)器、內(nèi)存頁等。

3、加載核心庫并初始化線程庫

4、初始化全局?jǐn)?shù)據(jù),這步完成后就可以創(chuàng)建java子線程了

5、初始化類加載器、解析器、編譯器、GC等模塊。其中重要一點就是初始化universe類型,這種類型是java種一切類型的類型,是一種數(shù)據(jù)結(jié)構(gòu),所有java的存儲對象都用該類型類存儲。

6、加載并初始化基礎(chǔ)類庫,如Lang、System、reflect等包。

7、返回給調(diào)用者。

通過上面的步驟,可以發(fā)現(xiàn)基礎(chǔ)類庫是在初始化階段完成加載的,這跟開發(fā)人員編寫的類庫加載順序是不同的。

jvm的關(guān)閉

當(dāng)java程序結(jié)束,jvm會先檢查有無未處理的異常以及清理這些異常,然后調(diào)用本地方法斷開主線程跟本地方法接口的連接,如果可以斷開,說明已經(jīng)沒有線程在運行了,則可以安全的關(guān)閉jvm。

和JNI_CreateJavaVM方法對應(yīng)的是DestroyJavaVM方法,當(dāng)jvm在啟動和運行時發(fā)生錯誤,根據(jù)嚴(yán)重程度會調(diào)用該方法關(guān)閉jvm,而在理想狀態(tài)下,即正常運行直到退出,也是調(diào)用DestroyJavaVM方法關(guān)閉并銷毀jvm。停止jvm按照以下主要步驟進行:

1、守護線程一致等待,直到只有一個非守護線程執(zhí)行。

2、停止監(jiān)控、計數(shù)器等線程。

3、移除當(dāng)前線程,釋放保護頁。

4、釋放所有資源,返回到調(diào)用者。

我們可以看出,當(dāng)需要關(guān)閉jvm時,如果jvm中仍有線程在運行,是無法強制關(guān)閉的,這就是為什么我們很多代碼的運行出現(xiàn)異常后,重復(fù)的調(diào)試導(dǎo)致有多個后臺jvm在運行卻不能自動結(jié)束而要手動關(guān)閉。

類加載機制

在前面說到,開發(fā)人員使用的類和基礎(chǔ)類庫并非同一時間加載的,這是有原因的。類的加載由類加載器來完成,包括加載、連接、初始化三個階段。完成加載后就可以通過new來創(chuàng)建類的實例對象了。類的加載可以理解為根據(jù)類的字節(jié)碼文件全路徑名讀取后轉(zhuǎn)換為與目標(biāo)類型一致的Class類型,并且是可以動態(tài)加載的。

加載類由類加載器完成,加載器分為兩種,一種是Bootstrap Classloader引導(dǎo)加載器,另一種是User-defined Classloader用戶自定義加載器,用戶自定義加載器默認(rèn)又分為ExtClassloader和AppClassloader。

引導(dǎo)加載器是C++編寫的,負(fù)責(zé)完成lib目錄里的類加載,也就是前面所說的基礎(chǔ)類庫,而ExtClassloader和AppClassloader是java編寫的,分別負(fù)責(zé)加載lib/ext目錄和ClassPath系統(tǒng)路徑中的類型。他們都是Classloader的子類,我們也可以通過繼承父類來實現(xiàn)自己的類加載器。

父類委托模式

通過查閱類關(guān)系樹可以發(fā)現(xiàn),AppClassloader是ExtClassloader的子類,而ExtClassloader則是Classloader的子類,java規(guī)范要求自定義的類加載器都派生與父類,并且在進行類加載的時候,都要委托給直接上級父類執(zhí)行加載,這就是父類委托模式(parents delegation model),國內(nèi)很多翻譯為雙親委托模式,但是你會發(fā)現(xiàn)是多親模式,所以我認(rèn)為父類委托更為合適。

父類委托模式在執(zhí)行時,子類始終會委托父類加載,一級一級的向上請求,知道最后唯一的超類來進行加載,如果父類無法加載,再一級一級的退回到子類進行加載,這樣就不會重復(fù)加載相同的類了。

為什么要使用父類委托模式?因為類的加載必須是一次性不可重復(fù)的,試想一下,如果基礎(chǔ)類庫中的類可以重復(fù)加另一個類來替換原來的類,那是多么嚴(yán)重的安全隱患,為了避免這一點,基礎(chǔ)類庫都是由C++編寫的啟動加載器來加載,但是為了兼顧擴展性,所以除了基礎(chǔ)類庫,其他的類都可以通過用戶加載器來加載,那么為了避免但不強制要求避免重復(fù)加載的情況發(fā)生,java規(guī)范就采取并建議我們按照父類委托的方式實現(xiàn)類加載器。

類的加載過程

前面說到,類先經(jīng)過類加載器將字節(jié)碼文件轉(zhuǎn)換為Class對象,但是這個時候并不能使用它,此時的類結(jié)構(gòu)信息存儲在方法區(qū)內(nèi),還需要對其進行驗證,結(jié)構(gòu)信息是否有效合法,一旦通過驗證,就會為類中的靜態(tài)變量分配內(nèi)存空間并初始化值,這些準(zhǔn)備工作完成后,還需將類結(jié)構(gòu)中的符號和常量表的符號進行解析轉(zhuǎn)為直接引用,這時候的類才具有執(zhí)行能力。最后的工作就是初始化了,也就是我們代碼中在new一個對象之前會執(zhí)行的static代碼塊。

垃圾回收機制

jvm的垃圾回收包括內(nèi)存動態(tài)分配和內(nèi)存回收兩大塊。內(nèi)存的分配和垃圾回收是息息相關(guān)的,內(nèi)存分配的方式一定程度上決定采取何種垃圾收集器和收集算法。

前面說到,堆內(nèi)存可以是連續(xù)也可以是不連續(xù)的,也是GC的重點區(qū)域,但正由于這種分布的不確定性,該GC帶來很大麻煩。首先針對連續(xù)的情況。

指針碰撞

通過前面講述的jvm啟動過程,我們知道創(chuàng)建對象就需要在堆內(nèi)存中劃分出一部分來存儲對象,如果此時的內(nèi)存是規(guī)整的,那么將空閑的和已使用的各放置一邊,兩部分的邊界處用一個指針標(biāo)記,當(dāng)新增對象內(nèi)存分配,就將指針偏移相應(yīng)的位置,下一次分配內(nèi)存只需要知道最后指針偏移的位置開始分配內(nèi)存并更新指針偏移量即可,這種方式就是指針碰撞(bump the pointer)。

空閑列表

然而,需要面臨的一個問題首先不是規(guī)整問題,而是線程安全,如果對指針的操作加鎖,必然會降低性能。并且如果堆不是連續(xù)的,指針碰撞就變得很棘手,此時還有一種解決辦法,就是通過一張表記錄下所有空閑的內(nèi)存,每當(dāng)分配內(nèi)存就更新表上的記錄,這種方式就是空閑列表(free list)。

不管呢種方式,都必須解決線程安全,對于指針碰撞,為了滿足規(guī)整的先決條件,這就要求GC收集器具有壓縮規(guī)整功能,如serial、par等收集器,而采用mark-sweep的cms這種收集器則不支持規(guī)整,因為他就是通過空閑列表方式來整理的內(nèi)存的。分配內(nèi)存就需要對內(nèi)存指針進行操作,如何確保指針的使用是線程安全的?一種做法是用過CAS原子操作來實現(xiàn),也就是所謂的失敗重試保證更新原子性。還有一種做法就是TLAB(本地線程緩沖),即在堆內(nèi)存中事先劃分一塊線程獨占的私有內(nèi)存,這樣線程就可以互不干涉的創(chuàng)建對象了,如果TLAB不夠用,再已加鎖的方式分配TLAB,并且對象的初始化還可以提前進行。

分代劃分收集機制

目前大部分的GC都是采用分代收集算法的,換而言之,也就是內(nèi)存是分代劃分的。這當(dāng)中的設(shè)計有很多復(fù)雜和嚴(yán)格的要求,首先對算法絕對精確,不能造成誤刪和誤讀,還要保證沒用的對象及時回收,以及如何處理產(chǎn)生的碎片和系統(tǒng)停頓開銷等。涉及的指標(biāo)和算法,就在另一篇中單獨闡述了。

待續(xù)......

到此這篇關(guān)于關(guān)于JVM工作原理簡述的文章就介紹到這了,更多相關(guān)JVM工作原理簡述內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實現(xiàn)修改PDF文件MD5值且保持內(nèi)容不變

    Java實現(xiàn)修改PDF文件MD5值且保持內(nèi)容不變

    在某些場景中,我們可能需要改變PDF文件的MD5值,而又不希望改變文件的可視內(nèi)容,本文詳細(xì)介紹了如何實現(xiàn)這一目標(biāo),并提供了具體的Java實現(xiàn)示例,需要的可以參考下
    2023-10-10
  • Java集合WeakHashMap源碼分析

    Java集合WeakHashMap源碼分析

    這篇文章主要介紹了Java集合WeakHashMap源碼分析,和HashMap一樣,WeakHashMap 也是一個散列表,它存儲的內(nèi)容也是鍵值對(key-value)映射,而且鍵和值都可以是null,需要的朋友可以參考下
    2023-09-09
  • springboot實現(xiàn)定時任務(wù)@Scheduled方式

    springboot實現(xiàn)定時任務(wù)@Scheduled方式

    這篇文章主要介紹了springboot實現(xiàn)定時任務(wù)@Scheduled方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring Boot集成Redis實現(xiàn)緩存機制(從零開始學(xué)Spring Boot)

    Spring Boot集成Redis實現(xiàn)緩存機制(從零開始學(xué)Spring Boot)

    這篇文章主要介紹了Spring Boot集成Redis實現(xiàn)緩存機制(從零開始學(xué)Spring Boot),需要的朋友可以參考下
    2017-04-04
  • AJAX中Get請求報錯404的原因以及解決辦法

    AJAX中Get請求報錯404的原因以及解決辦法

    剛學(xué)習(xí)一門技術(shù)時總會踩一些坑,下面這篇文章主要給大家介紹了關(guān)于AJAX中Get請求報錯404的原因及解決辦法的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • 當(dāng)Mybatis遇上目錄樹超全完美解決方案

    當(dāng)Mybatis遇上目錄樹超全完美解決方案

    這篇文章主要介紹了當(dāng)Mybatis遇上目錄樹有哪些解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • Spring中bean集合注入的方法詳解

    Spring中bean集合注入的方法詳解

    Spring作為項目中不可缺少的底層框架,提供的最基礎(chǔ)的功能就是bean的管理了。bean的注入相信大家都比較熟悉了,但是有幾種不太常用到的集合注入方式,可能有的同學(xué)會不太了解,今天我們就通過實例看看它的使用
    2022-07-07
  • Java中JDBC連接池的基本原理及實現(xiàn)方式

    Java中JDBC連接池的基本原理及實現(xiàn)方式

    本文詳細(xì)講解了Java中JDBC連接池的基本原理及實現(xiàn)方式,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • 公共POI導(dǎo)出Excel方法詳解

    公共POI導(dǎo)出Excel方法詳解

    這篇文章主要介紹了公共POI導(dǎo)出Excel方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Spring+SpringMVC+JDBC實現(xiàn)登錄的示例(附源碼)

    Spring+SpringMVC+JDBC實現(xiàn)登錄的示例(附源碼)

    這篇文章主要介紹了Spring+SpringMVC+JDBC實現(xiàn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05

最新評論