java 虛擬機原理

来源:https://www.cnblogs.com/cambodia/archive/2020/01/04/12149892.html
-Advertisement-
Play Games

什麼是JVM JVM是Java Virtual Machine(Java虛擬機)的縮寫,是一個虛構出來的電腦,它屏蔽了與具體操作系統平臺相關的信息,使得Java程式只需生成在Java虛擬機上運行的目標代碼(位元組碼,ByteCode), 就可以在多種平臺上不加修改地運行。這背後其實就是JVM把位元組碼 ...


什麼是JVM

JVM是Java Virtual Machine(Java虛擬機)的縮寫,是一個虛構出來的電腦,它屏蔽了與具體操作系統平臺相關的信息,使得Java程式只需生成在Java虛擬機上運行的目標代碼(位元組碼,ByteCode), 就可以在多種平臺上不加修改地運行。這背後其實就是JVM把位元組碼翻譯成具體平臺上的機器指令,從而實現“一次編寫,到處運行(Write Once, Run Anywhere)”。
Java為什麼能夠跨平臺?
Java引入了位元組碼的概念,jvm 只能認識位元組碼,並將它們解釋到系統的API調用。針對不同的系統有不同的jvm實現,有 Linux 版本的 jvm 實現,也有 Windows 版本的 jvm 實現,但是同一段代碼在編譯後的位元組碼是一樣的。在不同的系統平臺上運行是通過JAVA解釋器將位元組碼解釋為不同平臺的機器碼,在不同的 jvm 實現上會映射到不同系統的 API 調用,從而實現代碼的不加修改即可跨平臺運行。

JVM、JRE、JDK的關係

  1. JRE(Java Runtime Environment,Java運行環境),面向Java程式的使用者,而不是開發者。JRE是運行Java程式所必須環境的集合,包含JVM標準實現及 Java核心類庫。它包括Java虛擬機、Java平臺核心類和支持文件
  2. JDK(Java Development Kit,Java開發工具包),包括了Java運行環境(JRE),並提供了一堆Java工具tools.jar和Java標準類庫 (rt.jar)

三者的關係是:JDK>JRE>JVM

java虛擬機運行原理

按照階段分為兩個階段:
編譯階段:當我們將一個.java的文件進行編譯,編譯程式會生成一個相同名字而尾碼為.class的文件。
運行階段主要分為以下步驟:

  1. 載入
    • 通過一個類的全限定名來獲取該類的二進位位元組流
    • 將這個位元組流的靜態存儲結構轉化為方法區運行時數據結構
    • 在記憶體堆中生成一個代表該類的java.lang.Class對象,作為該類數據的訪問入口
  2. 驗證
    驗證、準備、解析這三步可以看做是一個連接的過程,將類的位元組碼連接到JVM的運行狀態之中
    驗證是為了確保Class文件的位元組流中包含的信息符合當前虛擬機的要求,不會威脅到jvm的安全,主要包括以下幾個方面的驗證:

    • 文件格式的驗證,驗證位元組流是否符合Class文件的規範,是否能被當前版本的虛擬機處理
    • 元數據驗證,對位元組碼描述的信息進行語義分析,確保符合java語言規範
    • 位元組碼驗證 通過數據流和控制流分析,確定語義是合法的,符合邏輯的
    • 符號引用驗證 這個校驗在解析階段發生
  3. 準備
    為類的靜態變數分配記憶體,初始化為系統的初始值。對於final static修飾的變數,直接賦值為用戶的定義值。如下麵的例子:這裡在準備階段過後的初始值為0,而不是7
    java public static int a=7
  4. 解析

    解析是將常量池內的符號引用轉為直接引用(如物理記憶體地址指針)

  5. 初始化

    到了初始化階段,jvm才真正開始執行類中定義的java代碼
    1)初始化階段是執行類構造器()方法的過程。類構造器()方法是由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊(static塊)中的語句合併產生的。
    2)當初始化一個類的時候,如果發現其父類還沒有進行過初始化、則需要先觸發其父類的初始化。
    3)虛擬機會保證一個類的()方法在多線程環境中被正確加鎖和同步。

JVM記憶體分區

  1. 程式計數器

    程式計數器(Progarm Counter Register)是一塊較小的記憶體空間,它可以看作是當前線程所執行的位元組碼行號指示器。在JVM中,通過程式計數器來記錄某個線程的位元組碼執行位置,或者說記錄下一條要運行的指令。程式計數器是具備線程隔離的特性,也就是說,每個線程工作時都有屬於自己的獨立計數器,互不影響,是一塊線程私有的記憶體空間。
    如果當前正在執行的是一個java方法,程式計數器會記錄正在執行的java位元組碼地址;如果正在執行的是native方法,則程式計數器為空。

  2. Java虛擬機棧

    java虛擬機棧是線程私有的記憶體空間,它用來保存方法的局部變數、部分結果,並參與方法的調用和返回。

    虛擬機棧在運營師採用棧幀來保存數據,棧幀中主要有局部變數表、操作數棧、動態鏈接地址、返回地址等信息。每一個方法的調用都伴隨著棧幀的入棧操作,相應的,方法的返回則對應著棧幀的出戰操作。

    和java棧相關的兩個異常:
    1. StackOverFlowError
      線上程的計算過程中,如果請求的棧的深度大於最大可用的棧深度,則拋出改異常。
    2. OutOfMemoryError
      如果java的棧可以擴展,在程式運行過程中,沒有足夠的記憶體來支撐程式的擴展,則拋出該異常。
  3. 本地方法棧
    本地方法棧和java虛擬機棧功能類似,本地方法棧主要管理本地方法棧的調用,一般是指有C實現的。和java虛擬機棧一樣會拋出StackOverFlowError和OutOfMemoryError異常

  4. 方法區

    方法區是java記憶體區域中比較重要的一部分,主要保存的信息是元數據。其中最為重要的是類的類型信息、常量池、域信息、方法信息。

  5. Java堆
    Java堆可以說是Java運行時記憶體中最為重要的一部分,幾乎所有的對象和數據都是在堆中分配空間的。Java堆分為新生代和老年代兩個部分,新生代用於存放剛剛產生的對象,如果對象一直沒有被回收,生存的足夠長,老年對象就會被移入老年代。
    新生代又可以細分為eden、surivor space0(s0或者from space)和surivor space1(s1或者To space)。eden存放剛剛創建的對象,s0和s1存放的對象至少經歷了一次垃圾回收,等幸存下來。如果幸存去的對象到了指定年齡仍未被回收,就會進入老年代。
    持久代:Permanent Generation。在Sun的JVM中就是方法區的意思,儘管有些JVM大多沒有這一代。主要存放常量及類的一些信息預設最小值為16MB,最大值為64MB

垃圾收集演算法

  • Mark-Sweep(標記-清除)演算法
    分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收所有被標記的對象。
    • 缺點:空間問題,標記清除之後會產生大量不連續的記憶體碎片,空間碎片太多可能會導致以後在程式運行過程中需要分配較大對象時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作;
    • 優點:簡單快速
  • Copying(複製)演算法

    它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的對象複製到另外一塊上面,然後再把已使用過的記憶體空間一次清理掉。
    • 缺點:記憶體使用率只有一半
    • 優點:不會產生碎片
  • Mark-Compact(標記-整理)演算法
    標記過程仍然與“標記-清除”演算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的記憶體,

  • 分代收集演算法
    當前商業虛擬機的垃圾收集都採用“分代收集”(Generational Collection)演算法,根據對象存活周期的不同將記憶體劃分為幾塊並採用不用的垃圾收集演算法。
    一般是把 Java 堆分為新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用複製演算法,只需要付出少量存活對象的複製成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記—清理”或者“標記—整理”演算法來進行回收。

垃圾收集器

  • Serial收集器

    新生代收集器,使用停止複製演算法,使用一個線程進行GC,串列,其它工作線程暫停。
  • ParNew收集器

    新生代收集器,使用停止複製演算法,Serial收集器的多線程版,用多個線程進行GC,並行,其它工作線程暫停,關註縮短垃圾收集時間。
  • Parallel Scavenge 收集器
    新生代收集器,使用停止複製演算法,關註CPU吞吐量,即運行用戶代碼的時間/總時間,比如:JVM運行100分鐘,其中運行用戶代碼99分鐘,垃 圾收集1分鐘,則吞吐量是99%,這種收集器能最高效率的利用CPU,適合運行後臺運算(關註縮短垃圾收集時間的收集器,如CMS,等待時間很少,所以適 合用戶交互,提高用戶體驗)。
  • Serial Old收集器

    老年代收集器,單線程收集器,串列,使用標記整理(整理的方法是Sweep(清理)和Compact(壓縮),清理是將廢棄的對象幹掉,只留幸存的對象,壓縮是將移動對象,將空間填滿保證記憶體分為2塊,一塊全是對象,一塊空閑)演算法,使用單線程進行GC,其它工作線程暫停(註意,在老年代中進行標記整理演算法清理,也需要暫停其它線程),在JDK1.5之前,Serial Old收集器與ParallelScavenge搭配使用。
  • Parallel Old收集器

    老年代收集器,多線程,並行,多線程機制與Parallel Scavenge差不錯,使用標記整理(與Serial Old不同,這裡的整理是Summary(彙總)和Compact(壓縮),彙總的意思就是將幸存的對象複製到預先準備好的區域,而不是像Sweep(清理)那樣清理廢棄的對象)演算法,在Parallel Old執行時,仍然需要暫停其它線程。Parallel Old在多核計算中很有用。Parallel Old出現後(JDK 1.6),與Parallel Scavenge配合有很好的效果,充分體現Parallel Scavenge收集器吞吐量優先的效果。
  • cms(concurrent mark sweep)收集器


    老年代收集器,致力於獲取最短回收停頓時間(即縮短垃圾回收的時間),使用標記清除演算法,多線程,優點是併發收集(用戶線程可以和GC線程同時工作),停頓小。使用-XX:+UseConcMarkSweepGC進行ParNew+CMS+Serial Old進行記憶體回收,優先使用ParNew+CMS(原因見後面),當用戶線程記憶體不足時,採用備用方案Serial Old收集。

  • G1收集器
    1. 初始標記階段僅僅只是標記一下 GC Roots 能直接關聯到的對象,並且修改 TAMS(Next Top at Mark Start)的值,讓下一階段用戶程式併發運行時,能在正確可用的 Region 中創建新對象,這階段需要停頓線程,但耗時很短。

    2. 併發標記階段是從 GC Root 開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程式併發執行。

    3. 而最終標記階段則是為了修正在併發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄線上程 Remembered Set Logs 裡面,最終標記階段需要把 Remembered Set Logs 的數據合併到 Remembered Set 中,這階段需要停頓線程,但是可並行執行。

    4. 最後在篩選回收階段首先對各個 Region 的回收價值和成本進行排序,根據用戶所期望的 GC 停頓時間來制定回收計劃,從Sun公司透露出來的信息來看,這個階段其實也可以做到與用戶程式一起併發執行,但是因為只回收一部分 Region,時間是用戶可控制的,而且停頓用戶線程將大幅提高收集效率。通過下圖可以比較清楚地看到G1收集器的運作步驟中併發和需要停頓的階段。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.python中函數的工作原理 python的解釋器,也就是python.exe(c編寫)會用PyEval_EvalFramEx(c函數)運行foo()函數 首先會創建一個棧幀(stack Frame),在棧幀對象的上下文裡面去運行這個位元組碼。 可以嘗試著去列印foo的位元組碼: 關於位元組碼的解釋: ...
  • python中的多線程其實並不是真正的多線程,如果想要充分地使用多核CPU資源,在python中大部分情況需要使用多進程。python提供了非常好用的多進程包Multiprocessing,只需要定義一個函數,python會完成其它所有事情。藉助這個包,可以輕鬆完成從單進程到併發執行的轉換。mult ...
  • 1 #include"stdio.h" 2 #include"stdlib.h" 3 #include"string.h" 4 #include"conio.h" 5 typedef struct node 6 { 7 char xh[11]; //學號 8 char xm[10]; //姓名 9 ...
  • 前言 本文轉自松哥(網名:江南一點雨)的一篇實用入門文章,寫的挺好的,希望對各位有所幫助。 什麼是面霸?就是在面試中,神擋殺神佛擋殺佛,見招拆招,面到面試官自慚形穢自嘆不如!松哥希望本文能成為你面霸路上的墊腳石! 做 Java 開發,沒有人敢小覷 Spring Boot 的重要性,現在出去面試,無論 ...
  • 什麼是Spring Spring是一個開源的,輕量級Java開發框架; 其核心特性是可以用於開發任何 Java 應用程式,Spring 框架的目標是使 JavaEE應用程式的開發變得更加容易,核心概念是IOC和AOP;這也是學習Spring的重點所在; Spring不是針對某個具體功能,具體層級的框 ...
  • 功能描述: 1)使用tkinter設計程式界面; 2)調用Windows API函數實現錄音機和音樂播放器。 。 參考代碼: ​ 運行界面: ​ ...
  • 不知不覺,一年一度的春運搶票大幕已經拉開,想快速搶到回家的車票嗎?作為程式員,這些技術手段,你一定要知道。 為了讓大家更快捷更便利的搶火車票,各種各樣的搶票軟體應需而生,這類軟體大部分都是付費搶票的機制。 作為程式員,如何用技術手段搶到回家的票?來看看用 Python 寫的搶票腳本。 手把手教你用 ...
  • float和double都是C++中的浮點型數據類型,三者的區別是:1、精度是不同的。float類型是單精度浮點數,double類型是雙精度浮點數。2、分配存儲空間。c++編譯器為flaot類型分配4位元組,為double類型分配8位元組。3、有效位的個數是不同的。float可以提供6位有效數字,dou ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...