什麼是JVM?

来源:https://www.cnblogs.com/chenshengjava/archive/2018/04/24/8934178.html
-Advertisement-
Play Games

說明:做java開發的幾乎都知道jvm這個名詞,但是由於jvm對實際的簡單開發的來說關聯的還是不多,一般工作個一兩年(當然不包括愛學習的及專門做性能優化的什麼的),很少有人能很好的去學習及理解什麼是jvm,以及弄清楚jvm的工作原理,個人認為這塊還是非常有必要去認真瞭解及學習的,特別是剛入門或入門不 ...


說明:做java開發的幾乎都知道jvm這個名詞,但是由於jvm對實際的簡單開發的來說關聯的還是不多,一般工作個一兩年(當然不包括愛學習的及專門做性能優化的什麼的),很少有人能很好的去學習及理解什麼是jvm,以及弄清楚jvm的工作原理,個人認為這塊還是非常有必要去認真瞭解及學習的,特別是剛入門或入門不久的java開發來說,這是java的基石。

JVM(Java Virtual Machine,Java虛擬機)

Java程式的跨平臺特性主要是指位元組碼文件可以在任何具有Java虛擬機的電腦或者電子設備上運行,Java虛擬機中的Java解釋器負責將位元組碼文件解釋成為特定的機器碼進行運行。因此在運行時,Java源程式需要通過編譯器編譯成為.class文件。眾所周知java.exe是java class文件的執行程式,但實際上java.exe程式只是一個執行的外殼,它會裝載jvm.dll(windows下,下皆以windows平臺為例,linux下和solaris下其實類似,為:libjvm.so),這個動態連接庫才是java虛擬機的實際操作處理所在。

JVM是JRE的一部分。它是一個虛構出來的電腦,是通過在實際的電腦上模擬模擬各種電腦功能來實現的。JVM有自己完善的硬體架構,如處理器、堆棧、寄存器等,還具有相應的指令系統。Java語言最重要的特點就是跨平臺運行。使用JVM就是為了支持與操作系統無關,實現跨平臺。所以,JAVA虛擬機JVM是屬於JRE的,而現在我們安裝JDK時也附帶安裝了JRE(當然也可以單獨安裝JRE)。

JVM記憶體區域劃分

粗略分來,JVM的內部體繫結構分為三部分,分別是:類裝載器(ClassLoader)子系統,運行時數據區,和執行引擎。

類裝載器

每一個Java虛擬機都由一個類載入器子系統(class loader subsystem),負責載入程式中的類型(類和介面),並賦予唯一的名字。每一個Java虛擬機都有一個執行引擎(execution engine)負責執行被載入類中包含的指令。JVM的兩種類裝載器包括:啟動類裝載器和用戶自定義類裝載器,啟動類裝載器是JVM實現的一部分,用戶自定義類裝載器則是Java程式的一部分,必須是ClassLoader類的子類。

執行引擎:它或者在執行位元組碼,或者執行本地方法

主要的執行技術有:解釋,即時編譯,自適應優化、晶元級直接執行其中解釋屬於第一代JVM,即時編譯JIT屬於第二代JVM,自適應優化(目前Sun的HotspotJVM採用這種技術)則吸取第一代JVM和第二代JVM的經驗,採用兩者結合的方式 。

自適應優化:開始對所有的代碼都採取解釋執行的方式,並監視代碼執行情況,然後對那些經常調用的方法啟動一個後臺線程,將其編譯為本地代碼,併進行仔細優化。若方法不再頻繁使用,則取消編譯過的代碼,仍對其進行解釋執行。

運行時數據區:主要包括:方法區,堆,Java棧,PC寄存器,本地方法棧

  • 方法區和堆由所有線程共用

堆:存放所有程式在運行時創建的對象

方法區:當JVM的類裝載器載入.class文件,併進行解析,把解析的類型信息放入方法區。

  • Java棧和PC寄存器由線程獨享

JVM棧是線程私有的,每個線程創建的同時都會創建JVM棧,JVM棧中存放的為當前線程中局部基本類型的變數(java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double)、部分的返回結果以及Stack Frame,非基本類型的對象在JVM棧上僅存放一個指向堆上的地址

  • 本地方法棧:存儲本地方法調用的狀態

JVM運行時數據區

因為jvm運行時的數據區對我們開發來說還是特別重要要掌握的知識所以單拎開來西說下。

  • 方法區域(Method Area)

在Sun JDK中這塊區域對應的為PermanetGeneration,又稱為持久代。

方法區域存放了所載入的類的信息(名稱、修飾符等)、類中的靜態變數、類中定義為final類型的常量、類中的Field信息、類中的方法信息,當開發人員在程式中通過Class對象中的getName、isInterface等方法來獲取信息時,這些數據都來源於方法區域,同時方法區域也是全局共用的,在一定的條件下它也會被GC,當方法區域需要使用的記憶體超過其允許的大小時,會拋出OutOfMemory的錯誤信息。

  • 堆(Heap)

它是JVM用來存儲對象實例以及數組值的區域,可以認為Java中所有通過new創建的對象的記憶體都在此分配,Heap中的對象的記憶體需要等待GC進行回收。

堆是JVM中所有線程共用的,因此在其上進行對象記憶體的分配均需要進行加鎖,這也導致了new對象的開銷是比較大的。

Sun Hotspot JVM為了提升對象記憶體分配的效率,對於所創建的線程都會分配一塊獨立的空間TLAB(Thread Local Allocation Buffer),其大小由JVM根據運行的情況計算而得,在TLAB上分配對象時不需要加鎖,因此JVM在給線程的對象分配記憶體時會儘量的在TLAB上分配,在這種情況下JVM中分配對象記憶體的性能和C基本是一樣高效的,但如果對象過大的話則仍然是直接使用堆空間分配。

TLAB僅作用於新生代的Eden Space,因此在編寫Java程式時,通常多個小的對象比大的對象分配起來更加高效。

  • JavaStack(java的棧):虛擬機只會直接對Javastack執行兩種操作:以幀為單位的壓棧或出棧

每個幀代表一個方法,Java方法有兩種返回方式,return和拋出異常,兩種方式都會導致該方法對應的幀出棧和釋放記憶體。

幀的組成:局部變數區(包括方法參數和局部變數,對於instance方法,還要首先保存this類型,其中方法參數按照聲明順序嚴格放置,局部變數可以任意放置),操作數棧,幀數據區(用來幫助支持常量池的解析,正常方法返回和異常處理)。

  • ProgramCounter(程式計數器)

每一個線程都有它自己的PC寄存器,也是該線程啟動時創建的。PC寄存器的內容總是指向下一條將被執行指令的餓地址,這裡的地址可以是一個本地指針,也可以是在方法區中相對應於該方法起始指令的偏移量。

若thread執行Java方法,則PC保存下一條執行指令的地址。若thread執行native方法,則Pc的值為undefined

  • Nativemethodstack(本地方法棧):保存native方法進入區域的地址

依賴於本地方法的實現,如某個JVM實現的本地方法藉口使用C連接模型,則本地方法棧就是C棧,可以說某線程在調用本地方法時,就進入了一個不受JVM限制的領域,也就是JVM可以利用本地方法來動態擴展本身。

JVM垃圾回收

Sun的JVMGenerationalCollecting(垃圾回收)原理是這樣的:把對象分為年青代(Young)、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的演算法。(基於對對象生命周期分析)

通常我們說的JVM記憶體回收總是在指堆記憶體回收,確實只有堆中的內容是動態申請分配的,所以以上對象的年輕代和年老代都是指的JVM的Heap空間,而持久代則是之前提到的MethodArea,不屬於Heap。

GC的基本原理:將記憶體中不再被使用的對象進行回收,GC中用於回收的方法稱為收集器,由於GC需要消耗一些資源和時間,Java在對對象的生命周期特征進行分析後,按照新生代、舊生代的方式來對對象進行收集,以儘可能的縮短GC對應用造成的暫停

(1)對新生代的對象的收集稱為minor GC;

(2)對舊生代的對象的收集稱為Full GC;

(3)程式中主動調用System.gc()強制執行的GC為Full GC。

不同的對象引用類型, GC會採用不同的方法進行回收,JVM對象的引用分為了四種類型:

(1)強引用:預設情況下,對象採用的均為強引用(這個對象的實例沒有其他對象引用,GC時才會被回收)

(2)軟引用:軟引用是Java中提供的一種比較適合於緩存場景的應用(只有在記憶體不夠用的情況下才會被GC)

(3)弱引用:在GC時一定會被GC回收

(4)虛引用:由於虛引用只是用來得知對象是否被GC

  • Young(年輕代)

年輕代分三個區。一個Eden區,兩個Survivor區。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被覆制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象將被覆制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的並且此時還存活的對象,將被覆制年老區(Tenured。需要註意,Survivor的兩個區是對稱的,沒先後關係,所以同一個區中可能同時存在從Eden複製過來對象,和從前一個Survivor複製過來的對象,而複製到年老區的只有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空的。

  • Tenured(年老代)

年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。

  • Perm(持久代)

用於存放靜態文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=進行設置。

我有一個微信公眾號,經常會分享一些Java技術相關的乾貨;如果你喜歡我的分享,可以用微信搜索“Java團長”或者“javatuanzhang”關註。


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

-Advertisement-
Play Games
更多相關文章
  • 一、文件的打開與關閉 1. 文件的打開 在python,使用open函數,可以打開一個已經存在的文件,或者創建一個新文件。 示例如下: f = open('test.txt', 'w') 2. 文件的關閉 示例如下: 註意:文件打開,執行必要的操作後必須要關閉。 但是我們總是經常忘記關閉它,怎麼辦呢 ...
  • 之前的文章中,分別從APS,排產到規劃引擎敘述了一些理論基礎;並介紹了一些Optaplanner大概的情況;並一步步將Optaplanner的示例運行起來,將示例源碼導進Eclipse分析了一下它的Hello world入門示例,從本篇開始,我們將分步學習它的一些概念及用法。 什麼是Optaplan ...
  • java中的一些概念彙總 什麼是Java虛擬機?為什麼Java被稱作是“平臺無關的編程語言”? Java虛擬機是一個可以執行Java位元組碼的虛擬機進程。Java源文件被編譯成能被Java虛 擬機執行的位元組碼文件。 Java被設計成允許應用程式可以運行在任意的平臺,而不需要程式員為每一個平臺單獨重 寫 ...
  • <! more 關註我 轉載請務必註明原創地址為: "http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/" 項目結構 結構分析: + Spring boot project 核心代碼,代碼量很多(197508 行) + Spring b ...
  • 前言 線程間的通信主要通過共用對欄位的訪問和對象引用欄位的引用,可能會產生兩種錯誤,線程干擾和記憶體一致性錯誤。Java的同步就是防止這些錯誤,但當多個線程訪問同一資源會導致線程執行緩慢,甚至暫停執行。 線程干擾(Thread Interference) 例子 class Counter { priv ...
  • Go語言聖經-介面1.介面類型是對其它類型行為的抽象和概括2.Go語言中介面類型的獨特之處在於它是滿足隱式實現的3.Go語言中還存在著另外一種類型:介面類型。介面類型是一種抽象的類型4.一個類型可以自由的使用另一個滿足相同介面的類型來進行替換被稱作可替換性(LSP里氏替換) 練習 7.1: 使用來自 ...
  • 今天開始自學Python。 找到神聖傳說中的Python官網:https://www.python.org/,安裝了Python3.6.5。 安裝步驟省略。 安裝requests庫: pip install requests 。 等待片刻即可安裝完畢。 打開IDLE: 測試requests庫是否安裝 ...
  • 在學習spring的時候出現瞭如下異常: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resourc ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...