1.前言
由于后期學習需要用到大量的JVM底層的東西劈狐,所有本人調(diào)整了一下學習計劃疙咸,打算先從JVM入手,了解整個JAVA的運行機制媳友,內(nèi)存模型斯议,編譯原理等等一些底層的東西,這樣在學習 后面的東西醇锚,會有一種豁然開朗的感覺哼御。后期的內(nèi)容有從網(wǎng)上直接復(fù)制粘貼的內(nèi)容,但是大部分的內(nèi)容都是經(jīng)過自己整理后的焊唬,我覺得參照別人寫的東西艇搀,未嘗不可。如果是轉(zhuǎn)載的文章求晶,最后我列出轉(zhuǎn)載的地址焰雕。雖然我做不了技術(shù)的創(chuàng)造者,但是爭取做一個好的傳播者芳杏。
2.什么是Java
經(jīng)過了多年的發(fā)展矩屁,Java早已由一門單純的計算機編程語言辟宗,演變?yōu)榱艘惶讖姶蟮?strong>技術(shù)體系。是的吝秕,什么是Java泊脐,我想技術(shù)體系四個字應(yīng)該是最好的概括了吧。Java設(shè)計者們將Java劃分為3種結(jié)構(gòu)獨立但卻彼此依賴的技術(shù)體系分支烁峭,它們分別對應(yīng)著不同的規(guī)范集合和組件:
1容客、Java SE(標準版),主要活躍在桌面領(lǐng)域约郁,主要包含了Java API組件缩挑。
2、Java EE(企業(yè)版)鬓梅,活躍在企業(yè)級領(lǐng)域供置,除了包含Java API組件外,還擴充有Web組件绽快、事務(wù)組件芥丧、分布式組件、EJB組件坊罢、消息組件等续担,綜合這些技術(shù),開發(fā)人員完全可以構(gòu)建出一個具備高性能活孩、結(jié)構(gòu)嚴謹?shù)钠髽I(yè)級應(yīng)用赤拒,并且Java EE也是用于構(gòu)建SOA(面向服務(wù)架構(gòu))的首選平臺。
3诱鞠、Java ME(精簡版)挎挖,活躍在嵌入式領(lǐng)域,稱之為精簡版的原因是航夺,它僅保留了Java API中的部分組件蕉朵,以及適應(yīng)設(shè)備的一些特有組件。
上面講到Java技術(shù)體系的分支阳掐,那既然Java是一種技術(shù)體系始衅,我們來看一下組成這種技術(shù)體系的技術(shù):
1、Java編程語言
2缭保、字節(jié)碼
3汛闸、Java API,包括Java API類庫和來自商業(yè)機構(gòu)以及開源社區(qū)的第三方類庫
4艺骂、Java虛擬機
很多時候我們只關(guān)注了第一點诸老,因為第一點才是和工作切實相關(guān)的。Java技術(shù)體系所包含的內(nèi)容實際上Java官方有提供給我們一張圖.
3.Java的優(yōu)點
Java能獲得如此廣泛的認可钳恕,除了它擁有一門結(jié)構(gòu)嚴謹别伏、面向?qū)ο蟮木幊陶Z言之外蹄衷,還有許多不可忽視的優(yōu)點:
1、它擺脫了硬件平臺的束縛厘肮,實現(xiàn)了“一次編寫愧口、到處運行”
2、它提供了一個相對安全的內(nèi)存管理和訪問機制类茂,避免了絕大部分的內(nèi)存泄露和指針越界問題
3耍属、它實現(xiàn)了熱點代碼檢測和運行時編譯及優(yōu)化,這使得Java應(yīng)用能隨著運行時間的增加而獲得更高的性能
4巩检、它有一套完整的應(yīng)用程序接口厚骗,還有無數(shù)來自商業(yè)機構(gòu)和開源社區(qū)的第三方類庫來幫助它實現(xiàn)各種各樣的功能
5、它與身俱來對分布式技術(shù)的支持就比較完善
但是碴巾,Java最大的優(yōu)勢和財富還不是以上這些,就像高翔龍老師在《Java虛擬機精講》中寫的丑搔,Java真正強大的地方是因為擁有全世界最多的技術(shù)擁護者和開源社區(qū)支持厦瓢,他們無時無刻都保持著最充沛的體力與思維,一步一步地驅(qū)動著Java技術(shù)的走向啤月。
4.JDK和JRE
兩個常見的重要概念煮仇。其實上面的圖中已經(jīng)劃分出了JDK和JRE的范圍了。我們對這張圖做一個歸納谎仲,用我們的語言簡單地總結(jié)一下什么是JDK和JRE:
1浙垫、JRE(Java Runtime Enviroment),是支持Java程序運行的標準環(huán)境,包含:java虛擬機郑诺、JAVA SE API夹姥、運行java應(yīng)用程序所必須的文件等。
2辙诞、JDK(Java Development Kit)辙售,是用于支持Java程序開發(fā)的最小環(huán)境,包含:JRE的部分飞涂,以及編譯器和調(diào)試器等旦部。
總結(jié):
如果只是要運行JAVA程序,只需要JRE就可以较店。 JRE通常非常小士八,也包含了JVM。
如果要開發(fā)JAVA程序梁呈,就需要安裝JDK婚度。
OpenJDK
前面有講過,“Java真正強大的地方是因為擁有全世界最多的技術(shù)擁護者和開源社區(qū)支持官卡,他們無時無刻都保持著最充沛的體力與思維陕见,一步一步地驅(qū)動著Java技術(shù)的走向”秘血。其實JDK在一開始并不是開源的,但是隨著開源運動的蓬勃發(fā)展评甜,2006年Sun公司宣布將對Java開放源代碼灰粮,開源的Java平臺開發(fā)主要集中在OpenJDK項目上。2009年4月15日忍坷,Sun公司正式發(fā)布OpenJDK粘舟,JDK 7則是Java開源后發(fā)布的第一個版本,任何組織和個人都可以為Java的發(fā)展做出貢獻佩研。當然OpenJDK和真正的Oracle JDK(因為Sun公司被Oracle公司在2010年收購了嘛柑肴,所以就叫做Oracle JDK了)還是有區(qū)別的:
OpenJDK中的代碼基本上都來自于Oracle JDK,屬于Oracle JDK的一個分支旬薯,但是其中去除了一些非開源的組件和代碼晰骑,替換成了開源的組件和代碼,主要是加密和圖形的部分绊序。因此用OpenJDK代替Oracle JDK可能會有一些的不兼容硕舆。
對于OpenJDK感興趣的,可以在OpenJDK官網(wǎng)http://download.java.net/openjdk/jdk7/下載OpenJDK的源代碼骤公。像Java虛擬機HotSpot抚官、Java編譯器Javac、JNI等等阶捆,源代碼都在里面凌节。
5.Java虛擬機(JVM)
5.1 概述
JVM是JRE的一部分,實際上它是一個虛構(gòu)出來的小型計算機洒试,通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的倍奢。JVM有自己完善的硬件架構(gòu),如處理器垒棋、堆棧娱挨、寄存器等,還具有相應(yīng)的指令系統(tǒng)捕犬。JAVA語言最大的特點就是跨平臺運行跷坝,即所謂的“一次編寫,處處運行”碉碉,它屏蔽了與具體操作系統(tǒng)平臺相關(guān)的信息柴钻,類似一個程序代碼和操作系統(tǒng)之間的一個中間件。
Java程序的跨平臺特性主要是指字節(jié)碼文件可以在任何具有Java虛擬機的計算機或者電子設(shè)備上運行垢粮,Java虛擬機中的Java解釋器負責將字節(jié)碼文件解釋成為特定的機器碼進行運行贴届。因此在運行時,Java源程序需要通過編譯器編譯成為.class文件。眾所周知java.exe是java class文件的執(zhí)行程序毫蚓,但實際上java.exe程序只是一個執(zhí)行的外殼占键,它會裝載jvm.dll(windows下,下皆以windows平臺為例元潘,linux下和solaris下其實類似畔乙,為:libjvm.so),這個動態(tài)連接庫才是java虛擬機的實際操作處理所在翩概。
5.2 JVM的主要功能
下面我們先看一張圖牲距,來了解JVM的主要功能和運行流程,如果看不懂沒關(guān)系钥庇,后期的文章會系列的討論牍鞠。
三項主要功能:
- 加載代碼:通過類加載器(class loader)加載類文件的過程,將class文件動態(tài)的加載到內(nèi)存中评姨。
- 運行時數(shù)據(jù)區(qū):JVM加載class文件和運行class文件的過程中难述,分配的空間,具體指上圖的jvm memory區(qū)域吐句。
- 執(zhí)行代碼:負責執(zhí)行class文件中包含的字節(jié)碼指令胁后。
5.3 加載
5.3.1 類加載的過程
下面簡單介紹一下類加載的過程,類從被加載到虛擬機內(nèi)存中開始蕴侧,到卸載出內(nèi)存為止择同,它的整個生命周期包括:加載两入、驗證净宵、準備、解析裹纳、初始化择葡、使用和卸載七個階段。它們開始的順序如下圖所示:
[圖片上傳失敗...(image-1617ab-1530860129287)]
其中**類加載的過程包括了加載剃氧、驗證敏储、準備、解析朋鞍、初始化五個階段**已添。其中驗證,準備滥酥,解析又可以合在一起更舞。
所以也可以分為三個大的步驟:裝載(Load),鏈接(Link)和初始化(Initialize)鏈接又分為三個步驟坎吻,如下圖所示:
[圖片上傳失敗...(image-992da7-1530860129287)]
1) 裝載:查找并加載類的二進制數(shù)據(jù)缆蝉;
2) 鏈接:
驗證:確保被加載類的正確性,就是確保.class字節(jié)碼符合虛擬機的要求;
準備:為類的靜態(tài)變量分配內(nèi)存刊头,并將其初始化為默認值,這些變量所使用的內(nèi)存都將在方法區(qū)中分配黍瞧;
解析:虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程;
3)初始化:初始化過程是一個執(zhí)行類構(gòu)造器<clinit>()方法的過程,根據(jù)程序員通過程序制定的主觀計劃去初始化類變量和其它資源原杂。其實就是執(zhí)行類構(gòu)造器方法的內(nèi)容护侮,包括給static變量賦予用戶指定的值以及執(zhí)行靜態(tài)代碼塊憎茂;
注意:
在這五個階段中,加載、驗證低千、準備和初始化這四個階段發(fā)生的順序是確定的,而解析階段則不一定奏属,它在某些情況下可以在初始化階段之后開始福压,這是為了支持Java語言的運行時綁定(也成為動態(tài)綁定或晚期綁定)。另外注意這里的幾個階段是按順序開始锐朴,而不是按順序進行或完成兴喂,因為這些階段通常都是互相交叉地混合進行的,通常在一個階段執(zhí)行的過程中調(diào)用或激活另一個階段焚志。
這里簡要說明下Java中的綁定:綁定指的是把一個方法的調(diào)用與方法所在的類(方法主體)關(guān)聯(lián)起來衣迷,對java來說,綁定分為靜態(tài)綁定和動態(tài)綁定:
- 靜態(tài)綁定:即前期綁定酱酬。在程序執(zhí)行前方法已經(jīng)被綁定壶谒,此時由編譯器或其它連接程序?qū)崿F(xiàn)。針對java膳沽,簡單的可以理解為程序編譯期的綁定汗菜。java當中的方法只有final,static挑社,private和構(gòu)造方法是前期綁定的陨界。
- 動態(tài)綁定:即后期綁定,也叫運行時綁定痛阻。在運行時根據(jù)具體對象的類型進行綁定菌瘪。在java中,幾乎所有的方法都是后期綁定的阱当。
5.3.2 類加載器
(1): 類加載器的結(jié)構(gòu)
說到加載俏扩,不得不提到類加載器,下面就具體講述下類加載器弊添。下面看一張圖了解一下類加載器的層次結(jié)構(gòu)录淡。
下面是類加載器的執(zhí)行的各自不同的目錄下的 .jar 包:
看上圖我們知道類加載器有以下幾類:
- 啟動類加載器:Bootstrap ClassLoader,在JVM啟動的時候加載表箭,使用native code實現(xiàn)(Hotspot虛擬機(C++))赁咙,是虛擬機自身的一部分钮莲。它負責加載java的核心庫,即存放在JDK\jre\lib(JDK代表JDK的安裝目錄彼水,下同)下崔拥,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機識別的類庫(如rt.jar凤覆,所有的java.*開頭的類均被Bootstrap ClassLoader加載)链瓦。此類加載器并不繼承java.lang.ClassLoader,不能被java程序直接調(diào)用盯桦。
- 擴展類加載器:Extension ClassLoader慈俯,該類由sun.misc.Launcher$ExtClassLoader實現(xiàn),它負責用于加載JAVA_HOME/lib/ext目錄中的拥峦,或者被java.ext.dirs系統(tǒng)變量指定所指定的路徑中所有類庫贴膘,就是除了基本的Java API以外的擴展類,比如 security的安全擴展功能略号,開發(fā)者可以直接使用擴展類加載器刑峡。
- 應(yīng)用程序類加載器:Application ClassLoader,一般也被稱為系統(tǒng)類加載器(System class loader)玄柠,該類加載器由sun.misc.LauncherAppClassLoader來實現(xiàn)突梦,它負責加載用戶類路徑(ClassPath)所指定的類(即bin目錄下的.class文件),開發(fā)者可以直接使用該類加載器羽利,如果應(yīng)用程序中沒有自定義過自己的類加載器宫患,一般情況下這個就是程序中默認的類加載器。
- 用戶自定義類加載器(User-defined class loader):這是應(yīng)用程序開發(fā)者用直接用代碼實現(xiàn)的類裝載器这弧。也可以用來加載應(yīng)用類娃闲,使用自定義的類加載器有很多特殊的原因:運行時重新加載類或者把加載的類分隔為不同的組,典型的用法比如 web 服務(wù)器 Tomcat当宴。
(2):類加載器的雙親委托模型
上面層次關(guān)系稱為類加載器的**雙親委派模型**畜吊。我們把每一層上面的類加載器叫做當前層類加載器的父加載器泽疆,當然户矢,它們之間的父子關(guān)系并不是通過繼承關(guān)系來實現(xiàn)的,而是**使用組合關(guān)系**來復(fù)用父加載器中的代碼殉疼。該模型在JDK1.2期間被引入并廣泛應(yīng)用于之后幾乎所有的Java程序中梯浪,但它并不是一個強制性的約束模型,而是Java設(shè)計者們推薦給開發(fā)者的一種類的加載器實現(xiàn)方式瓢娜。
**雙親委派模型的工作流程是**:如果一個類加載器收到了類加載的請求挂洛,它首先不會自己去嘗試加載這個類,而是把這個請求委托給父類加載器去完成眠砾,依次向上虏劲,層層遞進,最終所有的類加載請求最終都應(yīng)該被傳遞到頂層的啟動類加載器中,只有當父加載器在它的搜索范圍中沒有找到所需的類時柒巫,即無法完成該加載励堡,子加載器才會嘗試自己去加載該類。
優(yōu)點:
使用雙親委派模型來組織類加載器之間的關(guān)系堡掏,有一個很明顯的好處应结,就是Java類隨著它的類加載器(說白了,就是它所在的目錄)一起具備了一種帶有優(yōu)先級的層次關(guān)系泉唁,這對于保證Java程序的穩(wěn)定運作很重要鹅龄。例如,類java.lang.Object類存放在JDK\jre\lib下的rt.jar之中亭畜,因此無論是哪個類加載器要加載此類扮休,最終都會委派給啟動類加載器進行加載,這邊保證了Object類在程序中的各種類加載器中都是同一個類拴鸵。
(3): 類加載器的特點
Java提供了動態(tài)的加載特性肛炮;它會在運行時的第一次引用到一個class的時候?qū)λM行加載和鏈接,而不是在編譯期進行宝踪。JVM的類裝載器負責動態(tài)加載侨糟。
Java類裝載器有如下幾個特點:
- 層級結(jié)構(gòu):Java里的類裝載器被組織成了有父子關(guān)系的層級結(jié)構(gòu)。Bootstrap類裝載器是所有裝載器的父親瘩燥。
- 代理模式:基于層級結(jié)構(gòu)秕重,類的裝載可以在裝載器之間進行代理。當裝載器裝載一個類時厉膀,首先會檢查它是否在父裝載器中進行裝載了溶耘。如果上層的裝載器已經(jīng)裝載了這個類,這個類會被直接使用服鹅。反之凳兵,類裝載器會請求裝載這個類。
- 可見性限制:一個子裝載器可以查找父裝載器中的類企软,但是一個父裝載器不能查找子裝載器里的類庐扫。
- 不允許卸載:類裝載器可以裝載一個類但是不可以卸載它,不過可以刪除當前的類裝載器仗哨,然后創(chuàng)建一個新的類裝載器形庭。
5.4 運行時數(shù)據(jù)區(qū)
運行時數(shù)據(jù)區(qū)是在JVM運行的時候操作系統(tǒng)所分配的內(nèi)存區(qū)。它可以劃分為幾個區(qū)域:方法區(qū)厌漂,堆萨醒,java棧,pc寄存器苇倡,本地方法棧等富纸。
5.5 執(zhí)行引擎(****Execution Engine****)
5.5.1 執(zhí)行引擎的定義
通過類裝載器裝載的囤踩,被分配到JVM的運行時數(shù)據(jù)區(qū)的字節(jié)碼會被執(zhí)行引擎執(zhí)行。執(zhí)行引擎以指令為單位讀取Java字節(jié)碼晓褪。它就像一個CPU一樣高职,一條一條地執(zhí)行機器指令。每個字節(jié)碼指令都由一個1字節(jié)的操作碼和附加的操作數(shù)組成辞州。執(zhí)行引擎取得一個操作碼怔锌,然后根據(jù)操作數(shù)來執(zhí)行任務(wù),完成后就繼續(xù)執(zhí)行下一條操作碼变过。在執(zhí)行引擎執(zhí)行時埃元,有一個任務(wù)是必須把字節(jié)碼轉(zhuǎn)換成可以直接被JVM執(zhí)行的語言,也就是機器碼媚狰。
5.5.2 字節(jié)碼的執(zhí)行技術(shù)
主要的執(zhí)行技術(shù)有:解釋岛杀,即時編譯,AOT編譯等幾種崭孤。
其中編譯和解釋執(zhí)行的技術(shù)有時候是混合在一起的类嗤,如果單獨從編譯技術(shù)的角度來看,又分為:前端編譯辨宠、即時編譯(JIT編譯)遗锣、靜態(tài)提前編譯(AOT編譯)三種。
- 解釋器:在解釋執(zhí)行前嗤形,java編譯器已經(jīng)將java源碼文件(.java)編譯成class二進制字節(jié)碼文件精偿。解釋器一條一條的讀取二進制字節(jié)碼文件,解釋并且執(zhí)行字節(jié)碼指令赋兵。缺點是執(zhí)行速度比較慢.
- 即時(Just-In-Time)編譯器:即時編譯器被引入用來彌補解釋器的缺點笔咽。執(zhí)行引擎首先按照解釋執(zhí)行的方式來執(zhí)行,然后通過在運行時通過收集監(jiān)控信息霹期,檢查方法的執(zhí)行頻率叶组,如果一個方法的執(zhí)行頻率超過一個特定的值的話,那么這個方法就會被編譯成本地代碼历造,放到緩存里甩十,下次執(zhí)行引擎就沒有必要再去解釋執(zhí)行這個方法了,直接通過本地代碼去執(zhí)行它帕膜,因為本地代碼是保存在緩存里的枣氧。這種方式也被稱為“熱點”編譯溢十。它之所以被稱作”熱點“是因為熱點編譯器通過分析找到最需要編譯的“熱點”代碼垮刹,然后把熱點代碼編譯成本地代碼。如果已經(jīng)被編譯成本地代碼的字節(jié)碼不再被頻繁調(diào)用了张弛,換句話說荒典,這個方法不再是熱點了酪劫,那么Hotspot VM會把編譯過的本地代碼從cache里移除,并且重新按照解釋的方式來執(zhí)行它寺董。優(yōu)點是執(zhí)行效率大大增加覆糟,缺點就是編譯代碼所花的時間要比用解釋器去一條條解釋執(zhí)行花的時間要多。
- AOT(Ahead-Of-Time)編譯器遮咖。它使得多個JVM可以通過共享緩存來共享編譯過的本地代碼滩字,程序運行前,直接把Java源碼文件(.java)編譯成本地機器碼的過程御吞;
目前主要還是采用解釋器+JIT編譯這種混合的方式麦箍,如JDK中的HotSpot虛擬機。 AOT的編譯器在IBM JDK1.6的時候被引入進去陶珠,不過主流的jdk用的還是比較少挟裂。另外JIT編譯速度及編譯結(jié)果的優(yōu)劣,是衡量一個JVM性能的很重要的指標揍诽,****所以對程序運行性能優(yōu)化集中到這個階段诀蓉;****也就是說可以對這個階段進行JVM調(diào)優(yōu);
HotSpot JVM 內(nèi)置了兩個不同的即時編譯器暑脆,分別稱為Client Compiler(C1)和Server Compiler(C2)渠啤,HotSpot默認采用解釋器和其中一個編譯器直接配合的方式工作,使用哪個編譯器取決于虛擬機運行的模式添吗,HotSpot會根據(jù)自身版本和宿主機器硬件性能自動選擇模式C1還是C2埃篓,用戶也可以使用“-client”或”-server”參數(shù)去指定。
6.后記
研究jvm根资,并不是需要我們能寫一個jvm架专,而是要求我們最起碼對代碼的執(zhí)行有一個比較清晰的認識,當以后遇到程序比較復(fù)雜的場景玄帕,可以根據(jù)我們的業(yè)務(wù)需求定制自己的虛擬機部脚,對虛擬機進行調(diào)優(yōu)。
目前我們現(xiàn)在說的Java虛擬機基本上都是JDK自帶的虛擬機HotSpot裤纹,這款虛擬機也是目前商用虛擬中市場份額最大的一款虛擬機委刘,可以通過在命令行程序中輸入“java -version”來查看:
那其實市面上還有很多別的優(yōu)秀的虛擬機。Sun公司除了有大名鼎鼎的HotSpot外鹰椒,還有KVM锡移、Squawk VM、Maxine VM漆际,BEA公司有JRockit VM淆珊、IBM公司有J9 VM等等。
轉(zhuǎn)載地址:http://www.cnblogs.com/haitaofeiyang/p/7744620.html