1. 程式人生 > >一文讀懂JVM

一文讀懂JVM

一、什麼是JVM

JVM是Java Virtual Machine(Java 虛擬機器)的縮寫,JVM是一種用於計算裝置的規範,它是一個虛構出來的計算機,是通過在實際的計算機上模擬模擬各種計算機功能來實現的。

Java語言的一個非常重要的特點就是平臺無關性。而使用Java虛擬機器是實現這一特點的關鍵。一般的高階語言如果要在不同的平臺上執行,至少需要編譯成不同的目的碼。而引入Java語言虛擬機器後,Java語言在不同平臺上執行時不需要重新編譯。Java語言使用Java虛擬機器遮蔽了與具體平臺相關的資訊,使得Java語言編譯程式只需生成在Java虛擬機器上執行的目的碼(位元組碼),就可以在多種平臺上不加修改地執行。Java虛擬機器在執行位元組碼時,把位元組碼解釋成具體平臺上的機器指令執行。這就是Java的能夠“一次編譯,到處執行”的原因。

二、JVM總體概述

JVM總體上是由類裝載子系統(ClassLoader)、執行時資料區、執行引擎、垃圾收集這四個部分組成。其中我們最為關注的執行時資料區,也就是JVM的記憶體部分則是由方法區(Method Area)、JAVA堆(Java Heap)、虛擬機器棧(JVM Stack)、程式計數器、本地方法棧(Native Method Stack)這幾部分組成。

三、JVM體系結構

 

3.1 類裝載子系統

Class Loader類載入器負責載入.class檔案,class檔案在檔案開頭有特定的檔案標示,並且ClassLoader負責class檔案的載入等,至於它是否可以執行,則由Execution Engine決定。

3.2 執行時資料區

棧管執行,堆管儲存。JVM調優主要是優化Java堆和方法區。

3.2.1 方法區(Method Area)

方法區是各執行緒共享的記憶體區域,它用於儲存已被JVM載入的類資訊、常量、靜態變數、執行時常量池等資料。

3.2.2 Java堆(Java Heap)

Java堆是各執行緒共享的記憶體區域,在JVM啟動時建立,這塊區域是JVM中最大的, 用於儲存應用的物件和陣列,也是GC主要的回收區,一個 JVM 例項只存在一個堆記憶體,堆記憶體的大小是可以調節的。類載入器讀取了類檔案後,需要把類、方法、常變數放到堆記憶體中,以方便執行器執行,堆記憶體分為三部分:新生代、老年代、永久代。

說明:

  • Jdk1.6及之前:常量池分配在永久代 。
  • Jdk1.7:有,但已經逐步“去永久代” 。
  • Jdk1.8及之後:無永久代,改用元空間代替(java.lang.OutOfMemoryError: PermGen space,這種錯誤將不會出現在JDK1.8中)。

3.2.3 Java棧(JVM Stack)

1) 棧是什麼

Java棧是執行緒私有的,是線上程建立時建立,它的生命期是跟隨執行緒的生命期,執行緒結束棧記憶體也就釋放,對於棧來說不存在垃圾回收問題,只要執行緒一結束該棧就Over,生命週期和執行緒一致。基本型別的變數和物件的引用變數都是在函式的棧記憶體中分配。

2) 棧儲存什麼

每個方法執行的時候都會建立一個棧幀,棧幀中主要儲存3類資料:

  • 區域性變量表:輸入引數和輸出引數以及方法內的變數;
  • 棧操作:記錄出棧和入棧的操作;
  • 棧幀資料:包括類檔案、方法等等。

3) 棧執行原理

棧中的資料都是以棧幀的格式存在,棧幀是一個記憶體區塊,是一個數據集,是一個有關方法和執行期資料的資料集。每一個方法被呼叫直至執行完成的過程,就對應著一個棧幀在棧中從入棧到出棧的過程。

 

4) 本地方法棧(Native Method Stack)

本地方法棧和JVM棧發揮的作用非常相似,也是執行緒私有的,區別是JVM棧為JVM執行Java方法(也就是位元組碼)服務,而本地方法棧為JVM使用到的Native方法服務。它的具體做法是在本地方法棧中登記native方法,在執行引擎執行時載入Native Liberies.有的虛擬機器(比如Sun Hotpot)直接把兩者合二為一。

5) 程式計數器(Program Counter Register)

程式計數器是一塊非常小的記憶體空間,幾乎可以忽略不計,每個執行緒都有一個程式計算器,是執行緒私有的,可以看作是當前執行緒所執行的位元組碼的行號指示器,指向方法區中的方法位元組碼(下一個將要執行的指令程式碼),由執行引擎讀取下一條指令。

6) 執行時常量池

執行時常量池是方法區的一部分,用於存放編譯器生成的各種字面量和符號引用,這部分內容將在類載入後存放到方法區的執行時常量池中。相較於Class檔案常量池,執行時常量池更具動態性,在執行期間也可以將新的變數放入常量池中,而不是一定要在編譯時確定的常量才能放入。最主要的運用便是String類的intern()方法。

 

3.3 執行引擎(Execution Engine)

執行引擎執行包在裝載類的方法中的指令,也就是方法。執行引擎以指令為單位讀取Java位元組碼。它就像一個CPU一樣,一條一條地執行機器指令。每個位元組碼指令都由一個1位元組的操作碼和附加的運算元組成。執行引擎取得一個操作碼,然後根據運算元來執行任務,完成後就繼續執行下一條操作碼。

不過Java位元組碼是用一種人類可以讀懂的語言編寫的,而不是用機器可以直接執行的語言。因此,執行引擎必須把位元組碼轉換成可以直接被JVM執行的語言。位元組碼可以通過以下兩種方式轉換成合適的語言:

  • 直譯器: 一條一條地讀取,解釋並執行位元組碼執行,所以它可以很快地解釋位元組碼,但是執行起來會比較慢。這是解釋執行語言的一個缺點。
  • 即時編譯器:用來彌補直譯器的缺點,執行引擎首先按照解釋執行的方式來執行,然後在合適的時候,即時編譯器把整段位元組碼編譯成原生代碼。然後,執行引擎就沒有必要再去解釋執行方法了,它可以直接通過原生代碼去執行。執行原生代碼比一條一條進行解釋執行的速度快很多,編譯後的程式碼可以執行的很快,因為原生代碼是儲存在快取裡的。

 

3.4 垃圾收集(Garbage Collection, GC)

3.4.1 什麼是垃圾收集

垃圾收集即垃圾回收,簡單的說垃圾回收就是回收記憶體中不再使用的物件。所謂使用中的物件(已引用物件),指的是程式中有指標指向的物件;而未使用中的物件(未引用物件),則沒有被任何指標給指向,因此佔用的記憶體也可以被回收掉。

垃圾回收的基本步驟分兩步:

  • 查詢記憶體中不再使用的物件(GC判斷策略)
  • 釋放這些物件佔用的記憶體(GC收集演算法)

3.4.2 GC判斷策略

1) 引用計數演算法

引用計數演算法是給物件新增一個引用計數器,每當有一個引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器都為0的物件就是不可能再被使用的物件。缺點:很難解決物件之間相互迴圈引用的問題。

2) 根搜尋演算法

根搜尋演算法的基本思路就是通過一系列名為“GC Roots”的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain),當一個物件到GC Roots沒有任何引用鏈相連(也就是說從GC Roots到這個物件不可達)時,則證明此物件是不可用的。

在Java語言裡,可作為GC Roots的物件包括以下幾種:

  • 虛擬機器棧(棧幀中的本地變量表)中引用的物件;
  • 方法區中類靜態屬性引用的物件;
  • 方法區中常量應用的物件;
  • 本地方法棧中JNI(Native方法)引用的物件。

 

注:在根搜尋演算法中不可達的物件,也並非是“非死不可”的,因為要真正宣告一個物件死亡,至少要經歷兩次標記過程:第一次是標記沒有與GC Roots相連線的引用鏈;第二次是GC對在F-Queue執行佇列中的物件進行的小規模標記(物件需要覆蓋finalize()方法且沒被呼叫過)。

3.4.3 GC收集演算法

1) 標記-清除演算法(Mark-Sweep)

標記-清楚演算法採用從根集合(GC Roots)進行掃描,首先標記出所有需要回收的物件(根搜尋演算法),標記完成後統一回收掉所有被標記的物件。

 

該演算法有兩個問題:

  • 效率問題:標記和清除過程的效率都不高;
  • 空間問題:標記清除後會產生大量不連續的記憶體碎片, 空間碎片太多可能會導致在執行過程中需要分配較大物件時無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集。

2) 複製演算法(Copying)

複製演算法是將可用記憶體按容量劃分為大小相等的兩塊, 每次只用其中一塊, 當這一塊的記憶體用完, 就將還存活的物件複製到另外一塊上面, 然後把已使用過的記憶體空間一次清理掉。

 

3) 標記-整理演算法(Mark-Compact)

標記整理演算法的標記過程與標記清除演算法相同, 但後續步驟不再對可回收物件直接清理, 而是讓所有存活的物件都向一端移動,然後清理掉端邊界以外的記憶體。

 

4) 分代收集演算法(Generational Collection)

分代收集演算法是目前大部分JVM的垃圾收集器採用的演算法。它的核心思想是根據物件存活的生命週期將記憶體劃分為若干個不同的區域。一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。老年代的特點是每次垃圾收集時只有少量物件需要被回收,而新生代的特點是每次垃圾回收時都有大量的物件需要被回收,那麼就可以根據不同代的特點採取最適合的收集演算法。

 

新生代(Young Generation)的回收演算法(以複製演算法為主)

  • 所有新生成的物件首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。
  • 新生代記憶體按照8:1:1的比例分為一個eden區和兩個survivor(survivor0,survivor1)區。一個Eden區,兩個 Survivor區(一般而言)。大部分物件在Eden區中生成。回收時先將eden區存活物件複製到一個survivor0區,然後清空eden區,當這個survivor0區也存放滿了時,則將eden區和survivor0區存活物件複製到另一個survivor1區,然後清空eden和這個survivor0區,此時survivor0區是空的,然後將survivor0區和survivor1區交換,即保持survivor1區為空, 如此往復。
  • 當survivor1區不足以存放 eden和survivor0的存活物件時,就將存活物件直接存放到老年代。若是老年代也滿了就會觸發一次Full GC(Major GC),也就是新生代、老年代都進行回收。
  • 新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)。

老年代(Tenured Generation)的回收演算法(以標記-清除、標記-整理為主)

  • 在年輕代中經歷了N次垃圾回收後仍然存活的物件,就會被放到老年代中。因此,可以認為老年代中存放的都是一些生命週期較長的物件。
  • 記憶體比新生代也大很多(大概比例是1:2),當老年代記憶體滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代物件存活時間比較長,存活率標記高。

永久代(Permanet Generation)的回收演算法

用於存放靜態檔案,如Java類、方法等。永久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者呼叫一些class,例如Hibernate 等,在這種時候需要設定一個比較大的永久代空間來存放這些執行過程中新增的類。永久代也稱方法區。方法區主要回收的內容有:廢棄常量和無用的類。對於廢棄常量也可通過根搜尋演算法來判斷,但是對於無用的類則需要同時滿足下面3個條件:

  • 該類所有的例項都已經被回收,也就是Java堆中不存在該類的任何例項;
  • 載入該類的ClassLoader已經被回收;
  • 該類對應的java.lang.Class物件沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

3.4.4 垃圾收集器

1) Serial收集器(複製演算法)

新生代單執行緒收集器,標記和清理都是單執行緒,優點是簡單高效。是client級別預設的GC方式,可以通過-XX:+UseSerialGC來強制指定。

2) Serial Old收集器(標記-整理演算法)

老年代單執行緒收集器,Serial收集器的老年代版本。

3) ParNew收集器(停止-複製演算法)

新生代多執行緒收集器,其實就是Serial收集器的多執行緒版本,在多核CPU環境下有著比Serial更好的表現。

4) Parallel Scavenge收集器(停止-複製演算法)

新生代並行的多執行緒收集器,追求高吞吐量,高效利用CPU。吞吐量一般為99%, 吞吐量= 使用者執行緒時間/(使用者執行緒時間+GC執行緒時間)。適合後臺應用等對互動相應要求不高的場景。是server級別預設採用的GC方式,可用-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定執行緒數。

5) Parallel Old收集器(停止-複製演算法)

老年代並行的多執行緒收集器,Parallel Scavenge收集器的老年代版本,並行收集器,吞吐量優先。

6) CMS(Concurrent Mark Sweep)收集器(標記-清除演算法)

CMS收集器是一種以獲取最短回收停頓時間為目標的收集器,CMS收集器是基於“標記--清除”(Mark-Sweep)演算法實現的,整個過程分為四個步驟:

  • 初始標記: 標記GC Roots能直接關聯到的物件,速度很快;
  • 併發標記: 進行GC Roots Tracing的過程;
  • 重新標記: 修正併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分物件的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但比並發標記時間短;
  • 併發清除: 整個過程中耗時最長的併發標記和併發清除過程收集器執行緒都可以與使用者執行緒一起工作,所以,從總體上來說,CMS收集器的記憶體回收過程是與使用者執行緒一起併發執行的。
  • 優點:併發收集、低停頓
  • 缺點:對CPU資源非常敏感、無法處理浮動垃圾、產生大量空間碎片。

7) G1(Garbage First)收集器(標記-整理演算法)

G1是一款面向服務端應用的垃圾收集器,是基於“標記-整理”演算法實現的,與其他GC收集器相比,G1具備如下特點:

  • 並行與併發
  • 分代收集
  • 空間整合
  • 可預測性的停頓

G1運作步驟:

  • 初始標記(stop the world事件,CPU停頓只處理垃圾)
  • 併發標記(與使用者執行緒併發執行)
  • 最終標記(stop the world事件,CPU停頓處理垃圾)
  • 篩選回收(stop the world事件,根據使用者期望的GC停頓時間回收)

3.4.5 垃圾收集結構圖

相關推薦

JVM

一、什麼是JVM JVM是Java Virtual Machine(Java 虛擬機器)的縮寫,JVM是一種用於計算裝置的規範

大數據計算框架與平臺

ddr 不同 失敗 克服 可定制 同時 數據庫引擎 後處理 alc  1.前言   計算機的基本工作就是處理數據,包括磁盤文件中的數據,通過網絡傳輸的數據流或數據包,數據庫中的結構化數據等。隨著互聯網、物聯網等技術得到越來越廣泛的應用,數據規模不斷增加,TB、PB量級成為常

超簡單的 structured stream 源碼解讀

ket exec res exce bus sin imp += work 為了讓大家理解structured stream的運行流程,我將根據一個代碼例子,講述structured stream的基本運行流程和原理。 下面是一段簡單的代碼: 1 val spark =

Spring Boot、微服務架構和大數據治理之間的故事

Springboot微服務架構 微服務的誕生並非偶然,它是在互聯網高速發展,技術日新月異的變化以及傳統架構無法適應快速變化等多重因素的推動下誕生的產物。互聯網時代的產品通常有兩類特點:需求變化快和用戶群體龐大,在這種情況下,如何從系統架構的角度出發,構建靈活、易擴展的系統,快速應對需求的變化;同時,隨著用戶的

阻塞、非阻塞、同步、異步IO

UC max register class 掃描 基本 角度 cloud 問題: 介紹 在談及網絡IO的時候總避不開阻塞、非阻塞、同步、異步、IO多路復用、select、poll、epoll等這幾個詞語。在面試的時候也會被經常問到這幾個的區別。本文就來講一下這幾個詞

架構師都不知道的isinstance檢查機制

Python起步通過內建方法 isinstance(object, classinfo) 可以判斷一個對象是否是某個類的實例。但你是否想過關於鴨子協議的對象是如何進行判斷的呢? 比如 list 類的父類是繼 object 類的,但通過 isinstance([], typing.Iterable) 返回的卻是

【深度學習】機器學習常用損失函數(Loss Function)

back and 們的 wiki 導出 歐氏距離 classes 自變量 關於 最近太忙已經好久沒有寫博客了,今天整理分享一篇關於損失函數的文章吧,以前對損失函數的理解不夠深入,沒有真正理解每個損失函數的特點以及應用範圍,如果文中有任何錯誤,請各位朋友指教,謝謝~

從HTTP/0.9到HTTP/2:HTTP協議的歷史演變和設計思路

eight 結果 key 視頻 this sso單點登陸 會有 研究 patch 本文原作者阮一峰,作者博客:ruanyifeng.com。 1、引言 HTTP 協議是最重要的互聯網基礎協議之一,它從最初的僅為瀏覽網頁的目的進化到現在,已經是短連接通信的事實工業標準,最新版

以太坊代幣合約

規則 sta ini class 2015年 交易 存在 部分 生活 本文首發自 https://www.secpulse.com/archives/73696.html ,轉載請註明出處。 工欲善其事,必先利其器。要想挖掘和分析智能合約的漏洞,你必須要先學會看

什麽是音視頻直播雲服務 ?

type 限制 推流 數據 優缺點 視頻通訊系統 最終 通訊 地理 說到音視頻雲服務,大多數人可能聯想到的是網絡直播應用場景,實際上,硬件對音視頻雲服務的需求也在逐漸提升。而這樣的市場需求也推動了整個行業的發展,目前,阿裏雲、騰訊雲和網易雲等巨頭都已入局,除此之外還有即構科

機器學習大殺器XGBoost原理

結構 近似算法 機器 form con gin fff .cn tran http://blog.itpub.net/31542119/viewspace-2199549/ XGBoost是boosting算法的其中一種。Boosting算法的思想是將許多弱分類器集成在

什麽是Java中的自動拆裝箱

.com 空指針異常 http har 三目運算 容器 ava eof 關系 基本數據類型 基本類型,或者叫做內置類型,是Java中不同於類(Class)的特殊類型。它們是我們編程中使用最頻繁的類型。 Java是一種強類型語言,第一次申明變量必須說明數據類型,第一次變量

高性能網絡編程中的I/O模型

好的 min tcp 輸入 其中 那些事 follow hand 實現 1、前言 隨著互聯網的發展,面對海量用戶高並發業務,傳統的阻塞式的服務端架構模式已經無能為力。本文(和下篇《高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型》)旨在為大家提供有用的高性能網絡編程

充電寶usb接口電路及制作原理詳細

合規 其它 註意 pan 排列 ron 充電寶 需要 資料 轉自:http://www.elecfans.com/dianlutu/dianyuandianlu/20180511675801.html USB充電器套件,又名MP3/MP4充電器,輸入AC160-240V,5

AI網路結構:LeNet-5 AlexNet VGG Inception ResNet MobileNet

1 引言 當前深度學習十分火熱,深度學習網路模型對於降低錯誤率的重要作用不言而喻。深度學習應用場景主要分為三類:物體識別與分類,物體檢測,自然語言處理。在物體識別與分類領域,隨著AlexNet在2012年一炮走紅,深度學習重新燃起了一片熱情。從Lenet5第一次使用卷積開始,經歷了AlexNet

環形簽名技術

區塊鏈錢包人人都聽說過。實際上錢包這個詞並不十分恰當。在一般人的理解裡,錢包是用來存錢的,電子錢包——如支付寶等——也只是把現實中的貨幣電子化,本質上是一樣的,而區塊鏈錢包並非如此。區塊鏈錢包實際上並不儲存任何數字貨幣,它主要是用來儲存和管理私鑰,並通過私鑰進行交易。這樣看來,將其命名為“私鑰管理平

【機器學習】機器學習常用損失函式

損失函式(loss function)是用來估量模型的預測值f(x)與真實值Y的不一致程度,它是一個非負實值函式,通常使用L(Y, f(x))來表示,損失函式越小,模型的魯棒性就越好。損失函式是經驗風險函式的核心部分,也是結構風險函式重要組成部分。模型的結構風險函式包括了經驗風險項和正則項,通常可以

大資料技術驅動的銀行客戶畫像

2018年上半年我國GDP增速6.8%,在貿易戰和去槓桿的影響下,雖然金融市場震盪明顯,但銀行受到網際網路、移動計算到雲端計算、大資料、物聯網、人工智慧等技術變革的影響,仍舊保持了較好盈利。在採取了業務轉型與創新、同業業務從線下向線上轉移、資產結構的進一步優化等諸多調整措施的過程中,“手機銀行、P2

KMP 演算法

來源:阮一峰http://www.ruanyifeng.com/blog/2013/05/Knuth–Morris–Pratt_algorithm.html 字串匹配是計算機的基本任務之一。舉例來說,有一個字串"BBC ABCDAB ABCDABCDABDE",我想知道,裡面是否包

區塊鏈vs.DAG, 區別到底是什麼? 燒腦的資料結構之爭

作者 | 劉春明 DAG(有向無環圖)是一種非線性資料結構,可以替代區塊鏈,用於分散式賬本的儲存。這種結構在併發的場景下有更好的效能表現,但在實際應用中會面臨更多的技術挑戰。 其中,最大的挑戰在於,基於DAG結構實現智慧合約,要比基於區塊鏈結構困難得多。 本文將討論DAG和