需要再重新做一下筆記.....
1.jvm前言
作為Java工程師的你曾被傷害過嗎?你是否也遇到過這些問題?
運作着的線上系統突然卡死,系統無法通路,甚至直接OOM!
想解決線上JVM GC問題,但卻無從下手。
新項目上線,對各種JVM參數設定一臉茫然,直接預設吧然後就GG了
每次面試之前都要重新背一遍JVM的一些原理概念性的東西,然而面試官卻經常問你在實際項目中如何調優VM參數,如何解決GC、OOM等問題,一臉懵逼。
2.開發人員的病态
大部分Java開發人員,除了會在項目中使用到與Java平台相關的各種高精尖技術,對于Java技術的核心Java虛拟機了解甚少。
一些有一定工作經驗的開發人員,打心眼兒裡覺得SSM、微服務等上層技術才是重點,基礎技術并不重要,這其實是一種本末倒置的“病态”。如果我們把核心類庫的API比做數學公式的話,那麼Java虛拟機的知識就好比公式的推導過程。
計算機系統體系對我們來說越來越遠,在不了解底層實作方式的前提下,通過進階語言很容易編寫程式代碼。但事實上計算機并不認識進階語言
3.架構師在想什麼
架構師每天都在思考什麼?
應該如何讓我的系統更快?
如何避免系統出現瓶頸?
知乎上有條文章:應該如何看招聘資訊,直通年薪50萬+?
參與現有系統的性能優化,重構,保證平台性能和穩定性
根據業務場景和需求,決定技術方向,做技術選型
能夠獨立架構和設計海量資料下高并發分布式解決方案,滿足功能和非功能需求
解決各類潛在系統風險,核心功能的架構與代碼編寫
分析系統瓶頸,解決各種疑難雜症,性能調優等
4.為什麼學習jvm
面試的需要(BATJ、TMD,PKQ等面試都愛問)
中進階程式員必備技能:項目管理、調優的需求
追求極客的精神,比如:垃圾回收算法、JIT(即時編譯器)、底層原理
5.Java VS C++
垃圾收集機制為我們打理了很多繁瑣的工作,大大提高了開發的效率,但是,垃圾收集也不是萬能的,懂得JVM内部的記憶體結構、工作機制,是設計高擴充性應用和診斷運作時問題的基礎,也是Java工程師進階的必備能力。
C語言需要自己來配置設定記憶體和回收記憶體,Java全部交給JVM進行配置設定和回收。
6.TIOBE 排行榜
TIOBE 排行榜:https://www.tiobe.com/tiobe-index/
7.Java 生态圈
作為一個平台,Java虛拟機扮演着舉足輕重的作用
Groovy、Scala、JRuby、Kotlin等都是Java平台的一部分
作為一種文化,Java幾乎成為了"開源"的代名詞。
第三方開源軟體和架構。如Tomcat、Struts,MyBatis,Spring等。
就連JDK和JVM自身也有不少開源的實作,如openJDK、Harmony。
作為一個社群,Java擁有全世界最多的技術擁護者和開源社群支援,有數不清的論壇和資料。從桌面應用軟體、嵌入式開發到企業級應用、背景伺服器、中間件,都可以看到Java的身影。其應用形式之複雜、參與人數之衆多也令人咋舌。
8.Java的跨平台性
每個語言都需要轉換成符合java虛拟機規則位元組碼檔案,最後轉換的位元組碼檔案都能通過Java虛拟機進行運作和處理
随着Java7的正式釋出,Java虛拟機的設計者們通過JSR-292規範基本<code>實作在Java虛拟機平台上運作非Java語言編寫的程式</code>。
Java虛拟機根本不關心運作在其内部的程式到底是使用何種程式設計語言編寫的,它隻關心“位元組碼”檔案。也就是說Java虛拟機擁有語言無關性,并不會單純地與Java語言“終身綁定”,隻要其他程式設計語言的編譯結果滿足并包含Java虛拟機的内部指令集、符号表以及其他的輔助資訊,它就是一個有效的位元組碼檔案,就能夠被虛拟機所識别并裝載運作。
9.位元組碼
我們平時說的java位元組碼,指的是用java語言編譯成的位元組碼。準确的說任何能在jvm平台上執行的位元組碼格式都是一樣的。是以應該統稱為:jvm位元組碼。
不同的編譯器,可以編譯出相同的位元組碼檔案,位元組碼檔案也可以在不同的JVM上運作。
Java虛拟機與Java語言并沒有必然的聯系,它隻與特定的二進制檔案格式——Class檔案格式所關聯,Class檔案中包含了Java虛拟機指令集(或者稱為位元組碼、Bytecodes)和符号表,還有一些其他輔助資訊。
10.多語言混合程式設計
Java平台上的多語言混合程式設計正成為主流,通過特定領域的語言去解決特定領域的問題是目前軟體開發應對日趨複雜的項目需求的一個方向。
試想一下,在一個項目之中,并行處理用Clojure語言編寫,展示層使用JRuby/Rails,中間層則是Java,每個應用層都将使用不同的程式設計語言來完成,而且,接口對每一層的開發者都是透明的,各種語言之間的互動不存在任何困難,就像使用自己語言的原生API一樣友善,因為它們最終都運作在一個虛拟機之上。
對這些運作于Java虛拟機之上、Java之外的語言,來自系統級的、底層的支援正在迅速增強,以JSR-292為核心的一系列項目和功能改進(如DaVinci Machine項目、Nashorn引擎、InvokeDynamic指令、java.lang.invoke包等),推動Java虛拟機從"Java語言的虛拟機"向"多語言虛拟機"的方向發展。
11.自己寫個jvm
Java虛拟機非常複雜,要想真正了解它的工作原理,最好的方式就是自己動手編寫一個!
自己動手寫一Java虛拟機,難嗎?
天下事有難易乎?為之,則難者亦易矣;不為,則易者亦難矣
12.Java的重大事件
1990年,在Sun計算機公司中,由Patrick Naughton、MikeSheridan及James Gosling上司的小組Green Team,開發出的新的程式語言,命名為Oak,後期命名為Java
1995年,Sun正式釋出Java和HotJava産品,Java首次公開亮相。
1996年1月23日Sun Microsystems釋出了JDK 1.0。
1998年,JDK1.2版本釋出。同時,Sun釋出了JSP/Servlet、EJB規範,以及将Java分成了J2EE、J2SE和J2ME。這表明了Java開始向
企業、桌面應用和移動裝置應用3大領域挺進。
2000年,JDK1.3釋出,Java HotSpot Virtual Machine正式釋出,成為Java的預設虛拟機。
2002年,JDK1.4釋出,古老的Classic虛拟機退出曆史舞台。
2003年年底,Java平台的scala正式釋出,同年Groovy也加入了Java陣營。
2004年,JDK1.5釋出。同時JDK1.5改名為JavaSE5.0。
2006年,JDK6釋出。同年,Java開源并建立了OpenJDK。順理成章,Hotspot虛拟機也成為了OpenJDK中的預設虛拟機。
2007年,Java平台迎來了新夥伴Clojure。
2008年,oracle收購了BEA,得到了JRockit虛拟機。
2009年,Twitter宣布把背景大部分程式從Ruby遷移到Scala,這是Java平台的又一次大規模應用。
2010年,Oracle收購了Sun,獲得Java商标和最真價值的HotSpot虛拟機。此時,Oracle擁有市場占用率最高的兩款虛拟機
HotSpot和JRockit,并計劃在未來對它們進行整合:HotRockit
2011年,JDK7釋出。在JDK1.7u4中,正式啟用了新的垃圾回收器G1。
2017年,JDK9釋出。将G1設定為預設GC,替代CMS
同年,IBM的J9開源,形成了現在的Open J9社群
2018年,Android的Java侵權案判決,Google賠償Oracle計88億美元
同年,Oracle宣告JavagE成為曆史名詞JDBC、JMS、Servlet贈予Eclipse基金會
同年,JDK11釋出,LTS版本的JDK,釋出革命性的ZGC,調整JDK授權許可
2019年,JDK12釋出,加入RedHat上司開發的Shenandoah GC
13.虛拟機介紹
所謂虛拟機(Virtual Machine),就是一台虛拟的計算機。它是一款軟體,用來執行一系列虛拟計算機指令。大體上,虛拟機可以分為系統虛拟機和程式虛拟機。
系統虛拟機:大名鼎鼎的Virtual Box,VMware就屬于系統虛拟機,它們完全是對實體計算機的仿真,提供了一個可運作完整作業系統的軟體平台。
程式虛拟機:程式虛拟機的典型代表就是Java虛拟機,它專門為執行單個計算機程式而設計,在Java虛拟機中執行的指令我們稱為Java位元組碼指令。
無論是系統虛拟機還是程式虛拟機,在上面運作的軟體都被限制于虛拟機提供的資源中。
Java虛拟機是一台執行Java位元組碼的虛拟計算機,它擁有獨立的運作機制,其運作的Java位元組碼也未必由Java語言編譯而成。
JVM平台的各種語言可以共享Java虛拟機帶來的跨平台性、優秀的垃圾回器,以及可靠的即時編譯器。
Java技術的核心就是Java虛拟機(JVM,Java Virtual Machine),因為所有的Java程式都運作在Java虛拟機内部。
Java虛拟機就是二進制位元組碼的運作環境,負責裝載位元組碼到其内部,解釋/編譯為對應平台上的機器指令執行。每一條Java指令,
Java虛拟機規範中都有詳細定義,如怎麼取操作數,怎麼處理操作數,處理結果放在哪裡。
特點
<code>擺脫了平台的束縛,實作了“一次編譯,到處運作”的束縛</code>
<code>提供了一個相對安全的記憶體管理和通路機制,避免了絕大部分記憶體洩漏和指針越界問題</code>
<code>自動垃圾回收功能</code>
<code>實作了熱點代碼檢測和運作時編譯及優化,使得Java應用可以随着運作事件的增加而獲得更高的性能</code>
14.jvm的位置
JVM是運作在作業系統之上的,它與硬體沒有直接的互動Java的體系結構
15.Java的體系體系:
包括一下幾個組成部分:
Java程式設計語言
各種硬體平台上的Java虛拟機
Class檔案
Java Api類庫
來自商業機構和開源社群的第三方Java類庫
<code>我們把Java程式設計語言、Java虛拟機、Java Api類庫這三個部分稱為JDK</code>
<code>Java Api類庫中的 Java SE API 子集 和 Java虛拟機這兩部分統稱為JRE</code>
16.jvm的整體結構
<code>HotSpot VM</code>是目前市面上高性能虛拟機的代表作之一。
它采用解釋器與即時編譯器并存的架構。
在今天,Java程式的運作性能早已脫胎換骨,已經達到了可以和C/C++程式一較高下的地步。
執行引擎包含三部分:<code>解釋器</code>,<code>即時編譯器</code>,<code>垃圾回收器</code>
1)解釋器負責将加載到記憶體中的位元組碼檔案進行解釋運作,隻有解釋器、沒有JIT,JVM也可以運作,但是效率并不高,因為很多的代碼是重複執行的,沒有必要每次都重複解釋運作的過程,這時候就需要JIT。解釋器的内容後面再惡補吧!!!!
2)JIT将一些熱點代碼提前進行編譯(注意這裡的編譯和java到class的編譯是不一樣的,java到class的稱為編譯器前端,JIT的編譯稱為編譯器的後端,前端是将源檔案翻譯成位元組碼檔案用于跨平台,後端是将位元組碼翻譯成真正的作業系統可了解的機器語言,大家要注意),會極大的提升效率,但會帶來硬體上的開銷,HotSpot在解釋器和JIT之間進行了平衡。
3)垃圾回收器大家都知道是做什麼用的,在這就先不詳細去說了。
最後的本地方法接口和本地方法庫,用于調用其他語言中的類庫,例如調用C++編寫的功能。
作者:六塊巧克力
連結:https://zhuanlan.zhihu.com/p/256379202
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
17.Java代碼執行流程
最近也看的一系列部落格自己手動編寫一個簡單的解釋器 Part 6,有興趣的可以了解一下彙編語言
了解什麼是解釋器,什麼是解析器,什麼是詞法分析、什麼是文法分析、什麼是文法樹等..中間的圖需要再商讨下,,總感覺到哪裡不對
這裡再簡單回顧下Java代碼執行的整個過程,首先是前端編譯過程,java源檔案,通過前端編譯器,生成出位元組碼檔案(class檔案)。
前端編譯器,細化會拆分出詞法分析、文法分析、語義分析、位元組碼生成,這部分的内容不必過多研究,大緻了解下就可以,如果你學過彙編相關知識,會比較了解這一塊。
另外,如果你想自己開發一門屬于自己的語言,然後借助Java虛拟機去運作,那你則需要在這部分好好下下工具,去設計你自己的文法規範、開發自己的編譯器,然後隻要最終生成的位元組碼檔案複核JVM的規範,就可以在其上運作。
在編譯過程的衆多環節上,任何一個部分失敗,都将導緻最終的位元組碼檔案生成失敗。JAVA出于安全考慮,對于位元組碼檔案的要求非常高,隻有完全符合規範的class檔案才能被最終執行。
圖下面的java虛拟機部分就是上一節中所提到的,位元組碼解釋運作的過程,負責把位元組碼檔案翻譯成作業系統能夠看懂的機器指令。這裡要說一下,作業系統隻能夠看懂機器指令,目前的進階語言,需要先翻譯成彙編語言,然後再翻譯成機器指令,解釋器、JIT組成的執行引擎就是做這個的。
解釋器是負責響應時間,JIT主要負責性能。
JAVA代碼運作流程
執行引擎細節
連結:https://zhuanlan.zhihu.com/p/256394603
18.JVM架構模型
Java編譯器輸入的指令流基本上是一種<code>基于棧的指令集架構</code>,另外一種<code>指令集架構則是基于寄存器的指令集架構</code>。具體來說:這兩種架構之間的差別:
基于棧式架構的特點:
設計和實作更簡單,适用于資源受限的系統
避開了寄存器的配置設定難題:使用<code>零位址指令方式配置設定</code>
指令流中的指令大部分是零位址指令,其執行過程依賴于操作棧。指令集更小,編譯器容易實作
不需要硬體支援,可移植性更好,更好實作跨平台
基于寄存器架構的特點:
典型的應用是x86的二進制指令集:比如傳統的PC以及Android的Davlik虛拟機。
指令集架構則完全依賴硬體,與硬體的耦合度高,可移植性差
性能優秀和執行更高效
花費更少的指令去完成一項操作
在大部分情況下,<code>基于寄存器架構的指令集往往都以一位址指令、二位址指令和三位址指令為主</code>,而基于棧式架構的指令集卻是以<code>零位址指令</code>為主
示範1+1基于棧的指令集
兩條iconst_1指令連續把兩個常量壓入棧後,iadd指令把棧頂的兩個值出棧、相加、然後方位棧頂,最後istore_0把棧頂的值放到局部變量表的第0個solt中。
同樣示範1+1
mov指令把EAX寄存器的值設為1,然後add指令再把這個值加1,結果就儲存在EAX寄存器裡面。
兩種方式各有優缺點,基于棧的指令集很明顯可移植高,但是工作效率較低。而基于寄存器指令集寄存器由硬體直接提供,工作效率高,程式受硬體限制。
1
2
3
4
[止水輕揚2020-12-12
簡明扼要,真的很贊。一直有一個疑問,樓主幫忙解答一下:如文章所屬,我們知道Hotspot是基于棧的指令集,那麼對于這些java位元組碼指令,又是如何實作的呢?比如:iadd這個指令,我想最終還是逃離不了硬體層面的實作,是不是最終還是依托于寄存器指令集來實作?
備注:樓主的模拟程式很贊啊,友善介紹出處嗎??
贊回複踩 舉報
dddddd回複止水輕揚01-22
用X86指令實作就行了, iadd 翻譯成 add rax, rcx , 在C語言代碼層面可能就是 pop 兩個值 然後 int c = a + b , push(c) 就完事了
阿呆回複dddddd12 分鐘前
x86指令是基于寄存器的指令集吧? 是以止水輕楊問的你已經給出答案了嗎? 就是iadd這個指令,我想最終還是逃離不了硬體層面的實作。最終還是依托于寄存器指令集來實作
知乎上的問題,跟個後續12.基于棧的指令集與基于寄存器的指令集
問題2:看網友有人說
其中3說道
基于棧式架構的指令集在記憶體中操作,我有些不解.指令不是由cpu取指令由cpu執行的嗎??第三句話覺得有些不解?? 有大神知道可以告知嗎?出處JVM架構 |棧式指令集與寄存器指令集有什麼差別?
同樣執行2+3這種邏輯操作,其指令分别如下:
基于棧的計算流程(以Java虛拟機為例)
2.基于寄存器的計算流程
反編譯得到的指令
由于跨平台性的設計,Java的指令都是根據棧來設計的。不同平台CPU架構不同,是以不能設計為基于寄存器的。優點是跨平台,指令集小,編譯器容易實作,缺點是性能下降,實作同樣的功能需要更多的指令
面試題:時至今日,盡管嵌入式平台已經不是Java程式的主流運作平台了(準确來說應該是HotSpot VM的宿主環境已經不局限于嵌入式平台了),那麼為什麼不将架構更換為基于寄存器的架構呢?
答:因為基于棧的架構跨平台性好、指令集小,雖然相對于基于寄存器的架構來說,基于棧的架構編譯得到的指令更多,執行性能也不如基于寄存器的架構好,但考慮到其跨平台性與移植性,我們還是選用棧的架構
棧:跨平台性、指令集小、指令多;執行性能比寄存器差
19.jvm生命周期(待補充,覺得沒有說清楚)
虛拟機的啟動
Java虛拟機的啟動是通過<code>引導類加載器</code>(bootstrap class loader)建立一個初始類(initial class)來完成的,這個類是由虛拟機的具體實作指定的。
程式執行某個方法,首先需要将所在類加載到記憶體中,這是一個自定義類,這個類通過系統類加載器加載。父類是Object,Object需要被引導類加載器加載。除了Object類,程式運作還需要很多類的加載來實作,所有依賴的類都加載後,JVM才完成了啟動。最早加載的類稱為初始類。
<code>public class Test07 { public static void main(String[] args) { System.out.println("123"); } }</code>
虛拟機的執行
一個運作中的Java虛拟機有着一個清晰的任務:執行Java程式
程式開始執行時他才運作,程式結束時他就停止
<code>執行一個所謂的Java程式的時候,真真正正在執行的是一個Java虛拟機的程序</code>,這個大家可以寫一個簡單程式,休眠幾秒,看一下運作中的程序。
<code>public class Test07 { public static void main(String[] args) throws InterruptedException { Thread.sleep(10000); System.out.println( "done"); } }</code>
運作程式,在指令行視窗執行jps指令,可以看到運作中的程序:
當程式運作結束後,程序也就自然消失,是以說,java程式的運作,是JVM的程序在運作,如果程序終止,java程式也就随之停止。
虛拟機的退出
程式正常執行結束,比如上面的示例中,main方法全部執行完畢後,程式退出
程式在執行過程中遇到了異常或錯誤而異常終止
由于作業系統用現錯誤而導緻Java虛拟機程序終止
某線程調用Runtime類或<code>System類的exit( )</code>方法,或<code>Runtime類的halt( )</code>方法,并且Java安全管理器也允許這次exit( )或halt( )操作。
除此之外,JNI(Java Native Interface)規範描述了用JNI Invocation API來加載或解除安裝 Java虛拟機時,Java虛拟機的退出情況。