成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JAVA運行時簡述(HotSpot)

hosition / 1556人閱讀

摘要:拆解虛擬機的基本步聚如下首先,要等待到自身成為唯一一個正在運行的非守護線程時,在整個等待過程中,虛擬機仍舊是可工作的。將相應的事件發(fā)送給,禁用,并終止信號線程。

本文簡單介紹HotSpot虛擬機運行時子系統(tǒng),內(nèi)容來自不同的版本,因此可能會與最新版本之間(當前為JDK12)存在一些誤差。

1.命令行參數(shù)處理

HotSpot虛擬機中有大量的可影響性能的命令行屬性,可根據(jù)他們的消費者進行簡單分類:執(zhí)行器消費(如-server -client選項),執(zhí)行器處理并傳遞給JVM,直接由JVM消費(大多)。
這些選項可分為三個主要的類別:標準選項,非標準選項,開發(fā)者選項。標準選項是指所有的JVM不同實現(xiàn)均可以處理且在不同版本之間穩(wěn)定可用的選項(但是也可以deprecated)。
以-X開頭的選項是非標準選項(不保證所有JVM虛擬機的實現(xiàn)均支持),后續(xù)的JAVA SDK更新也不保證會對它進行通知。使用-XX開頭的為開發(fā)者選項,它一般需要特定的系統(tǒng)環(huán)境(以保證實現(xiàn)正確的操作)和足量的權(quán)限(以訪問系統(tǒng)配置參數(shù)),這些實現(xiàn)應當慎重使用,相應的選項在更新后也并不保證通知到用戶。
命令行參數(shù)控制了JVM內(nèi)部變量的屬性值,這些參數(shù)同時具備"類型"與"值"。對于布爾型的屬性值,以+或-置于參數(shù)之前,可分別表示該屬性的值為true或false。對于需要其他數(shù)據(jù)的變量,同樣有許多機制可以設(shè)置其值數(shù)據(jù)(很遺憾并不統(tǒng)一),一部分參數(shù)在格式上要求在屬性名稱后直接跟隨屬性值,有些不需要分隔符,有些又不得不加上分隔符,分隔符又可能是":","="等,如-XX:+OptionName, -XX:-OptionName, and -XX:OptionName=。
大多整型的參數(shù)(如內(nèi)存大?。┛山邮?k","m","g"分別代表kb,mb,gb這種簡寫形式。

2.虛擬機運行周期

執(zhí)行器
HotSpot虛擬機有幾種Java標準版的執(zhí)行器,在unix系統(tǒng)上即java命令,在windows系統(tǒng)下即java和javaw(javaw其實是指基于網(wǎng)絡(luò)的執(zhí)行器)。從屬于虛擬機啟動過程的執(zhí)行器操作有:
a.解析命令行選項,部分選項直接由執(zhí)行器自身消費,如-client和-sever屬性被用來決斷加載合適的vm庫,其他的屬性則作為虛擬機初始化參數(shù)(JavaVMInitArgs)傳遞給vm。
b.如果未明確指定選項,執(zhí)行器來確定堆的大小和編譯器類型(是client還是server)。
c.確立如LD_LIBRARY_PATH 和 CLASSPATH等環(huán)境變量。
d.如果未在命令行中明確指定主類,執(zhí)行器會從jar文件清單中找出主類名稱。
e.執(zhí)行器會在一個新創(chuàng)建的線程(非原生線程)中使用JNI_CreateJavaVM來創(chuàng)建虛擬機實例。 注意,在原生線程中創(chuàng)建vm會極大的減少定制vm的可能性,如windows中的棧大小等。
f.一旦vm創(chuàng)建并初始化成功,加載主類成功,執(zhí)行器可從主類中得到main方法的屬性,然后使用CallStaticVoidMethod執(zhí)行主方法并以命令行參數(shù)為它的方法入?yún)ⅰ?br> g.當java主方法執(zhí)行完成時,檢查和清理任何可能已發(fā)生的掛起的異常,返回退出狀態(tài)。它會使用ExceptionOccurred來清理異常,方法如果執(zhí)行成功,它會給調(diào)用進程返回一個0值,否則為其他值。
h.使用DetachCurrentThread解除主線程的關(guān)聯(lián),這樣減少了線程的數(shù)量,保證可安全調(diào)用DestroyJavaVM,它也能保證線程不在vm中執(zhí)行操作,棧中不再有存活的棧楨。

最重要的兩個階段是JNI_CreateJavaVM以及DestroyJavaVM,下面詳述。
JNI_CreateJavaVM執(zhí)行步驟:
首先保證沒有兩個線程同時調(diào)用此方法,從而保證不在同一進程中出現(xiàn)兩個vm實例。當在一個進程空間中達到一個初始化點時,該進程空間中不能再創(chuàng)建vm,該點也被稱為“不返回的點”(point of no return)。原因在于此時vm已創(chuàng)建的靜態(tài)數(shù)據(jù)結(jié)構(gòu)不能夠重新初始化。
接下來,要對JNI的版本支持進行檢查,檢測gc日志的ostream是否初始化。此時會初始化一些操作系統(tǒng)模塊,如隨機數(shù)生成器,當前進程號,高分辨率的時間,內(nèi)存頁大小和保護頁等。
解析傳入的參數(shù)和屬性值,并存放留用。初始化java標準系統(tǒng)屬性。
基于上一步解析的參數(shù)和屬性進一步創(chuàng)建和初始化系統(tǒng)模塊,這一次的初始化是為同步,棧內(nèi)存,安全點頁做準備。在此時如libzip,libhpi,libjava,libthread等庫也完成了加載,同時完成信號句柄的初始化和設(shè)定,并初始化線程庫。
接下來初始化輸出流日志,任何必需的代理庫(hprof jdi等)均于此時完成初始化和開啟。
完成線程狀態(tài)的初始化以及持有了線程操作所需的指定的數(shù)據(jù)的線程本地存儲(TLS Thread Local Storage)的初始化。
全局數(shù)據(jù)的初始化,如事件日志,操作系統(tǒng)同步,性能內(nèi)存(perfMemory),內(nèi)存分配器(chunkPool)等。
到此時開始創(chuàng)建線程,會創(chuàng)建java版本的主線程并綁定到一個當前操作系統(tǒng)線程上。然而這個線程還不能被Threads線程列表感知,完成java級別的線程初始化和啟用。
緊接著進行余下部分的全局模塊的初始化,它們包括啟動類加載器(BootClassLoader),代碼緩存(CodeCache),解釋器(Interpreter),編譯器(Compiler),JNI,系統(tǒng)字典(SystemDictionary),Universe。此時便已到達前述的“不返回的點”,也就是說,我們此時已不能在進程的地址空間中再創(chuàng)建一個vm實例了。
主線程會在此時被加入到線程列表中,這一步首先要對Thread_Lock進行加鎖操作。此處會對Universe(戲稱為小宇宙,即所需的全局數(shù)據(jù)結(jié)構(gòu))進行健全檢查。此時創(chuàng)建執(zhí)行所有重要vm函數(shù)的VMThread,創(chuàng)建完成后,即達到一個合適的點,在這個點,可發(fā)出適當?shù)腏VMTI事件通知當前jvm的狀態(tài)。
加載并初始化一些類,包含java.lang.String,java.lang.System,java.lang.Thread,java.lang.ThreadGroup,java.lang.reflect.Method,java.lang.ref.Finalizer,java.lang.Class,以及系統(tǒng)類中的其他成員。在這一刻,vm已經(jīng)完成初始化并且可操作,但是并未具備完整的功能。
到這一步,信號處理器線程也被開啟,同時也完成了啟動編譯器線程和CompileBroker線程,以及StatSampler和WatcherThreads等輔助線程,此時vm具備了完整的功能,生成JNIEnv信息并返回給調(diào)用者,此時的vm已經(jīng)準備就緒,可服務(wù)新的JNI請求。

DestroyJavaVM執(zhí)行步驟

DestroyJavaVM的調(diào)用有兩種情況:執(zhí)行器調(diào)用它拆解vm,或vm自身在出現(xiàn)嚴重錯誤時調(diào)用。拆解虛擬機的基本步聚如下:
首先,要等待到自身成為唯一一個正在運行的非守護線程時,在整個等待過程中,虛擬機仍舊是可工作的。
調(diào)用java.lang.Shutdown.shutdown()方法,它會執(zhí)行java級別的關(guān)閉勾子方法,如果有退出終結(jié)器可用,運行相應的終結(jié)器(finalizer)。
調(diào)用before_exit()為vm退出做出準備,運行vm級別的關(guān)閉勾子(它們是用JVM_OnExit()注冊的),停止剖析器(Profiler),采樣器(StatSampler),Watcher和GC線程。將相應的事件發(fā)送給JVMTI/PI,禁用JVMPI,并終止信號線程。
調(diào)用JavaThread的exit方法,釋放JNI句柄塊,移除棧保護頁,把此線程從線程列表中移除,從這個點起,任何java代碼不可被執(zhí)行。
終止vm線程,它會把當前的vm帶到安全點并終止編譯器線程。在安全點,應注意任何可能會在安全點阻塞的功能都不可使用。
禁用JNI/JVM/JVMPI屏障的追蹤。
給native代碼中依舊在運行的線程設(shè)置_vm_existed標記。
刪除這個線程。
調(diào)用exit_globals刪除IO和PerfMemory資源。
返回調(diào)用者。

3.虛擬機類加載(class loading)

虛擬機要負責常量池符號的解析,它需要對有關(guān)的類和接口先后進行裝載(loading),鏈接(linking)然后初始化。一般用“類加載機制”來描述把一個類或接口的名稱映射到一個class對象的過程,相應的,JVMS定義了詳細的裝載,鏈接和初始化階段的協(xié)議。
類的加載是在字節(jié)碼解析過程中完成的,典型是當一個類文件中的常量池符號需要被解析時。有一些JAVA的api會觸發(fā)這個過程,如Class.forName(),classLoader.loadClass(),反射api,以及JNI_FindClass均能初始化類的加載。虛擬機自身也能初始化類加載。虛擬機會在啟動時加載如Object,Thread等核心類。裝載一個類需要裝載所有的超類和超接口。且對于鏈接階段的類文件驗證過程,可能需要裝載額外的類。
虛擬機和JAVA SE類加載庫共同承擔了類的加載,虛擬機執(zhí)行了常量池的解析,類和接口的鏈接和初始化。加載階段是vm和特定的類加載器(java.lang.ClassLoader)之間的一個協(xié)作過程。

類加載階段

裝載階段,根據(jù)類或接口的名稱,在類文件中找出二進制語義,定義類并創(chuàng)建java.lang.Class對象。如果在類文件中找不到二進制表示,則拋出NoClassDefFound錯誤。此外裝載階段也做了一些類文件的在語法上的格式檢查,檢查不通過會拋出ClassFormatError或UnsupportedClassVersionError。在完成裝載之前,vm必須載入所有的超類和超接口。如果類繼承樹存在問題,如類直接或間接地自己繼承或?qū)崿F(xiàn)自己,則vm會拋出ClassCircularityError。若vm發(fā)現(xiàn)類的直接接口不是接口,或者直接父類是一個接口,則會拋出IncompatibleClassChangeError。
類加載的鏈接階段首先做一些校驗,它會檢測類文件的語義,常量池符號以及類型檢測,這個過程可能會拋出VerifyError。鏈接階段接下來進行一些準備工作,它會為靜態(tài)字段進行創(chuàng)建和初始化標準默認值,并分配方法表。注意,到此步為止不會進行任何java代碼的執(zhí)行。之后鏈接階段還有一個可選的步驟,即符號引用的解析。
接下來是類的初始化階段,它會運行類的靜態(tài)初始化器,初始化類的靜態(tài)字段。這是類的java代碼的第一次執(zhí)行。注意類的初始化需要超類的初始化,但不包含超接口的初始化。
JAVA虛擬機規(guī)范(JVMS)規(guī)定了類的初始化發(fā)生在類的第一次“活化使用”,java語言規(guī)范(JLS)允許鏈接階段的符號解析過程在不破壞java語義前提下的靈活性,裝載,鏈接和初始化的每一個步驟都要在前一步驟完成后進行。為了性能考慮,HotSpot虛擬機一般會等到要去初始化一個類時才會去進行類的裝載和鏈接。所以舉個簡單的例子,如果類A引用了類B,那么加載類A將不會必然導致B的加載,除非在驗證階段必需。當執(zhí)行了第一個引用B的指令時,將會導致B的初始化,而這又需要先對類B進行裝載和鏈接。

類加載的委托機制
當一個加載器被要求查找和加載一個class時,它可以請求另一個類加載器去做實際的加載工作。這個機制被稱為加載委托。第一個類加載器是一個“初始化加載器”,而最終定義了該類的類加載器被稱為“定義加載器”,在字節(jié)碼解析的例子中,初始化加載器負責該類的常量池符號的解析。
類加載器是分層定義的,每個類加載器可有委托的雙親。委托機制定義了二進制類表示的檢索順序。JAVASE類加載器按層序檢索啟動類加載器,擴展類加載器和系統(tǒng)類加載器。系統(tǒng)類加載器同時也是默認的應用類加載器,它會運行main方法并從類路徑下加載類。應用類加載器可以是JAVASE 類加載器庫中的實現(xiàn),也可以由應用開發(fā)人員實現(xiàn)。JAVASE類庫實現(xiàn)了擴展類加載器,它負責加載jre下lib/ext目錄中的類。
作者在“54個JAVA官方文檔術(shù)語”一文中曾說過,這一機制已經(jīng)不適用于JAVA9以上版本的描述,如果去查詢有關(guān)文章,可以發(fā)現(xiàn)這個經(jīng)典的類加載委托機制其實已經(jīng)歷過三次破壞(委托機制出廠時晚于加載器本身,破壞一;線程上下文類加載器,破壞二;熱部署的后門,破壞三),而作者個人認為類加載器支持JAVA9之后的模塊路徑的加載也是一種破壞,它們之間不再是簡單的委托加載,也不僅從類路徑下加載,不同路徑加載到的模塊也有不同的處理機制,詳細描述見該文。
啟動類加載器是由vm實現(xiàn)的,它從BOOTPATH下加載類,包含rt.jar中的類定義。為了快速啟動,vm也會通過類數(shù)據(jù)共享(cds)來處來類的預加載。關(guān)于cds,在最新的幾版jdk中有所更新,我們在稍后的章節(jié)中簡述。

類型安全
類或者接口名是由包含包名稱的全限定名定義的。一個類的類型由該全限定名和類加載器所唯一定義,所以類加載器其實可以理解為一個名稱空間,兩個不同類加載器定義的同一個類實際上會是兩個class類型。
vm會對自定義類加載器進行限定,保證不能與類型安全發(fā)生沖突。當類A中調(diào)用類B的方法時,vm通過追蹤和檢查加載器約束保證兩個類的加載器在方法參數(shù)和返回值上協(xié)商一致。

HotSpot中的類元數(shù)據(jù)
類加載的結(jié)果是在永久代(舊版)創(chuàng)建一個instanceKlass或者arrayKlass。instanceKlass指向一個java.lang.Class的實例,虛擬機c++代碼通過klassOop訪問instanceClass。
HotSpot內(nèi)部類加載數(shù)據(jù)

HotSpot虛擬機為了追蹤類加載而維護了三張主哈希表。分別是SystemDictionary表,它包含被加載的類,它們映射鍵為一個類名/類加載器對,值為一個klassOop,它同時包含了類名/初始化加載器對和類名/定義加載器對,目前只有在安全點才可以移除它們;PlaceholderTable表,它包含當前正在被載器的類,它被用于前述ClassCircularityError檢查和支持多線程類加載的加載器進行并行加載;LoaderConstraintTable,它追蹤類型安全檢查約束。這些哈希表都由一個鎖SystemDictionary_lock來保護,一般情況下vm中的類加載階段是使用類加載器對象鎖串行執(zhí)行的。

4.字節(jié)碼驗證和格式檢查

JAVA語言是類型安全的,標準的java編譯器會生產(chǎn)可用的類文件和類型安全的代碼,但是jvm不能保證代碼是由可信任的編譯器生成的,因此它必須在鏈接時進行字節(jié)碼校驗(bytecode verification)重建類型安全。
字節(jié)碼校驗的規(guī)范詳見java虛擬機規(guī)范的4.8節(jié)。規(guī)范中規(guī)定了JVM校驗的代碼動態(tài)和靜態(tài)約束。如果發(fā)現(xiàn)了任何與約束沖突的地方,虛擬機將會拋出VerifyError并阻斷類的鏈接。
可靜態(tài)檢查的字節(jié)碼約束有很多,"ldc"碼(Low Disparity Code 低差別編碼)的操作數(shù)必須為一個可用的常量池索引,它的類型是CONSTANT_Integer, CONSTANT_String 或 CONSTANT_Float。其他指令需要的檢查參數(shù)類型和個數(shù)的約束需要對代碼動態(tài)分析,這樣來決定執(zhí)行時哪個操作數(shù)可出現(xiàn)在表達式棧。
目前,有兩種辦法(截止1.6)分析字節(jié)碼并決定在每一條指定中出現(xiàn)的操作數(shù)類型和個數(shù)。傳統(tǒng)的辦法被稱作“類型推斷”,它通過對每個字節(jié)碼進行抽象解釋,在代碼的分支處或異常句柄處進行類型狀態(tài)的合并。整個分析過程會迭代全部的字節(jié)碼,直到發(fā)現(xiàn)這些類型的“穩(wěn)態(tài)”。如果不能達到穩(wěn)態(tài),或者結(jié)果類型與一些字節(jié)碼的約束沖突,那么拋出VerifyError。這一步的驗證代碼位于外部庫libverify.so中,它使用JNI去收集所需的類和類型的信息。
在JDK6中出現(xiàn)了第二種被稱為“類型驗證“的方法,在這種方法中,java編譯器通過代碼屬性,StackMapTable來提供每一個分支和異常目標的穩(wěn)態(tài)類型信息。StackMapTable包含大量的棧圖楨,每一個楨表示方法的某一個偏移量的表達式棧和局部變量表中的條目類型。jvm接下來只需要遍歷字節(jié)碼并驗證字其中的類型正確性。這是一個已經(jīng)在JAVAME CLDC中使用的技術(shù)。因為它小而快,此驗證方法vm自身即可構(gòu)建。
對于所有版本號低于50,創(chuàng)建早于JDK6的類文件,jvm會使用傳統(tǒng)的類型推薦方式驗證類文件,否則會使用新辦法。

5.類數(shù)據(jù)共享(cds)

類數(shù)據(jù)共享是一個JDK5引入的功能,旨在提高java程序語言應用的啟動時間,尤其是小型應用,同時,它也能減少內(nèi)存占用。當jre安裝在32位系統(tǒng)時并且使用sun提供的安裝器時,安裝器會從系統(tǒng)jar中載入一組類并生成一種內(nèi)部的格式,然后轉(zhuǎn)儲為一個文件,這個文件被稱作”共享存檔“。如果沒有使用sun提供的jre安裝器,也可以手動執(zhí)行。在后續(xù)的jvm執(zhí)行時,這個共享存檔文件被映射進內(nèi)存,節(jié)省了其他jvm裝載類和元數(shù)據(jù)的時間。
目前官方對于cds的文檔未整理完善,在截止到JAVA8的有關(guān)文檔中,仍可以見到這樣一句描述:Class data sharing is supported only with the Java HotSpot Client VM, and only with the serial garbage collector,即類共享目前只在HotSpot client虛擬機中支持,且只能使用serial垃圾收集器。而在JAVA9-12的若干新特性中,也對cds有過一些更新描述,如JAVA10中對類數(shù)據(jù)的共享包含了應用程序的類,在JAVA11中模塊路徑也支持了cds。但作者并未在專門的垃圾收集器中找到大篇幅的詳述,不過根據(jù)jdk12的jvm文檔中介紹,cds已經(jīng)在 G1, serial, parallel, 和 parallelOldGC 幾種垃圾收集器中支持,且默認使用G1的128M堆內(nèi)存。且G1在JDK7中已出現(xiàn),在JDK9中已經(jīng)成為默認的垃圾收集器,parallel 出現(xiàn)的相對更早,因此作者嚴重懷疑JAVA8中相應文檔描述的準確性,好在我們可以直接去看最新版。
cds可以減少啟動時間,因為它減少了裝載固定的類庫的開銷,應用程序相對于使用的核心類越小,cds就相當節(jié)省了越多的啟動時間。cds同時也有兩種方式減少了jvm實例的內(nèi)存占用。首先,一部分共享存檔文件被映射進內(nèi)存并作為只讀的庫,多個jvm進程不需要重復占用進程的內(nèi)存空間;其次,因為共享存檔文件中包含的類數(shù)據(jù)已經(jīng)是jvm使用的格式,處理rt.jar(低于9的版本)所需的額外內(nèi)存開銷也可以省去了,這使得多個應用在同一機器上能夠更優(yōu)的并發(fā)執(zhí)行。
在HotSpot虛擬機中,類共享的實現(xiàn)實際是在永久代(元空間)中開辟了新的內(nèi)存區(qū)域存放共享數(shù)據(jù)。存檔文件名為”classes.jsa“,它會在vm啟動時映射進這個空間。后續(xù)的管理由vm內(nèi)存管理子系統(tǒng)負責。
共享數(shù)據(jù)是只讀的,它包含常量方法對象(constMethodOops),符號對象(symbolOops),基本類型數(shù)組,多數(shù)字符數(shù)組??勺x寫的共享數(shù)據(jù)包含可變的方法對象(methodOops),常量池對象(constantPoolOops),vm內(nèi)部的java類和數(shù)組實現(xiàn)(instanceKlasses和arrayKlasses), 以及大量的String,Class和Exception對象。
作者看來,近幾版的jdk關(guān)于cds的幾處更新明顯借鑒了一些如tomcat等服務(wù)器的機制,適配越來越多的云生產(chǎn)環(huán)境,減少內(nèi)存開銷和啟動開銷都是為云用戶省錢的方式。

6.解釋器

當前HotSpot解釋器是一個基于模板的解釋器,它被用來執(zhí)行字節(jié)碼。HotSpot在啟動時運行時用InterpreterGenerator在內(nèi)存中利用TemplateTable(每個字節(jié)碼有關(guān)的匯編代碼)中的信息生成一個解釋器實例。模板是每個字節(jié)碼的描述,模板表定義了所有模板并提供了獲取指定字節(jié)碼的訪問方法。在jvm啟動時,可使用-XX:+PrintInterpreter打印有關(guān)的模板表信息。
執(zhí)行效果上看,模板好于經(jīng)典的switch語句循環(huán)的方式,原因也很簡單,首先switch語句執(zhí)行重復的比較操作來得到目標字節(jié)碼,最極端情況它可能需要對一個給定的指定比較所有的字節(jié)碼;第二,模板使用共享的棧來傳遞java參數(shù),同時本地c方法棧被vm自身來使用,大量的jvm內(nèi)部變量是用c變量存放的(如線程的程序計數(shù)器或棧指針),它們不保證永久存放在硬件寄存器中,管理這些軟件的解釋結(jié)構(gòu)會消耗總執(zhí)行時間中的相當可觀的一部分。
從全局來看,HotSpot解釋器大幅彌合了虛擬機和實體機器之間的裂縫,它大大加快了解釋的時間,但是犧牲了很多代碼的機器塊,同時也增大了代碼大小和復雜度,也需要一些代碼的動態(tài)生成。很明顯,debug機器動態(tài)生成的代碼要比靜態(tài)代碼更加困難。
對于一些對匯編語言來說過于復雜的操作,如常量池的查找,解釋器會運行時調(diào)用vm來完成。
HotSpot解釋器也是整個HotSpot自適應優(yōu)化歷史中重要的一部分,自適應優(yōu)化解決了JIT編譯的問題,大部分情況下,幾乎所有的程序都是用大量時間執(zhí)行極少量的代碼,因此運行時不需要逐方法編譯,vm僅使用解釋器來立即運行程序,分析代碼在程序中運行的次數(shù),避免編譯不頻繁運行的程序代碼(大多數(shù)),這樣HotSpot編譯器可以專注于程序中最需要性能優(yōu)化的部分,并不增加全局的編譯時間,在程序持續(xù)運行期間進行動態(tài)的監(jiān)控,達到最適應用戶需要的目的。

7.JAVA異常處理

jvm使用異常作為一個信號,它說明程序中出現(xiàn)了與java語言語義相沖突的事件,數(shù)組越界是一個極簡的案例。異常會導致控制流從異常發(fā)生或拋出的點轉(zhuǎn)到程序指定的處理點或捕獲點的一次非本地轉(zhuǎn)換。HotSpot解釋器和動態(tài)編譯器在運行時協(xié)作實現(xiàn)了異常的處理。異常處理有兩種簡單案例,異常拋出并由同一方法捕獲,異常拋出并由調(diào)用者捕獲。后一種情況稍微復雜一些,因為需要展開棧來找出恰當?shù)奶幚碚摺?br> 要初始化一個異常有多個方式,如throw字節(jié)碼,從vm內(nèi)部調(diào)用中返回,JNI調(diào)用中返回,或java調(diào)用中返回,最后一個情況其實是前三者的后一階段。當vm意識到有異常拋出時,執(zhí)行運行時系統(tǒng)去找出該異常最近的處理器,這一過程會用到三片信息:當前方法,當前字節(jié)碼,異常對象。如果當前方法沒有找到處理器,如上面提到的,將當前活化的棧楨出棧,進程將在此前的棧楨中迭代重復上述步驟。一旦找到了合適的處理器,vm更新執(zhí)行狀態(tài),跳轉(zhuǎn)到相應的處理器,java代碼在相應位置繼續(xù)執(zhí)行。

8.同步

廣泛來講,可以把“同步”定義為一個阻止或恢復不恰當?shù)牟l(fā)交互(一般稱為競態(tài))的一個機制。在java中,并發(fā)通過線程來表示,鎖排他是java中常見的一個同步案例,這一過程中,只有一個線程同時被允許訪問一段保護的代碼或數(shù)據(jù)。
HotSpot提供了java監(jiān)視器的概念,線程可通過監(jiān)視器來排它的運行應用代碼。監(jiān)視器只有兩個狀態(tài):鎖或者未鎖,一個線程可以在任何時間持有(鎖?。┍O(jiān)視器。只有在獲取了監(jiān)視器后,線程才能進入被監(jiān)視器保護的代碼塊。在java中這類被監(jiān)視器保護的代碼塊稱為同步代碼塊。
無競態(tài)的同步包含了大多的同步情況,它由常量時技術(shù)實現(xiàn)。java對于同步機制做了大量的優(yōu)化,偏向鎖技術(shù)是其中之一,因為大多數(shù)的對象一生只被最多一個線程持有鎖,因此允許該線程將監(jiān)視器偏向給自己,一旦偏向,該線程后續(xù)鎖和解鎖不再需要額外又昂貴的原子指令開銷。
對于有競態(tài)的同步操作場景,使用高級自適應自旋技術(shù)提高吞吐量。即使此時應用中有大量的競態(tài),在經(jīng)歷這些優(yōu)化后,同步操作性能已經(jīng)大幅提升,從jdk6開始,它不再是現(xiàn)在的real-world程序中的重大問題。
在HotSpot中,大多的同步操作是由一種被稱作“fast-path”(快路)代碼的調(diào)用完成的。有兩種即時編譯器(JIT)和一個解釋器,它們都可以產(chǎn)生快路代碼。兩種編譯器分別是C1,即-client編譯器,以及C2,即-server編譯器。C1和C2均直接在同步點生成快路代碼。在一般沒有競態(tài)的情況下,同步操作將會完全在快路中執(zhí)行,然而當發(fā)現(xiàn)需要去阻塞或者喚醒一個線程時(如monitorenter monitorexit),將會進入slow-path執(zhí)行,它由本地C++代碼實現(xiàn)。
單個對象的同步狀態(tài)是在對象中的第一個word中編碼存放的(mark word,詳見前面的文章“54個JAVA官方文檔術(shù)語”)。mark word對同步狀態(tài)元數(shù)據(jù)來說是多用的(其實mark word本身也是多用的,它還包含gc分代數(shù)據(jù),對象的hash碼值)。這些狀態(tài)包含:
Neutral(中立): 未鎖
Biased(偏向): 鎖/未鎖+非共享
Stack-Locked(棧鎖): 鎖+共享 無競態(tài)
Inflated(膨脹鎖): 鎖/未鎖+共享和競態(tài)

9.線程管理

線程管理覆蓋線程從創(chuàng)建到銷毀的整個生命周期,并負責在vm內(nèi)協(xié)調(diào)各個線程。這個過程包含java代碼創(chuàng)建的線程(應用代碼或庫代碼),綁定到vm的本地線程,出于各種目的創(chuàng)建的vm內(nèi)部線程。線程管理在絕大多數(shù)情況下是獨立于運行平臺的,但仍有一些細節(jié)與所運行的操作系統(tǒng)有所關(guān)聯(lián)。
線程模型
在hotspot虛擬機中,java線程和操作系統(tǒng)線程是一對一映射的關(guān)系,java線程即一個java.lang.Thread實例,當它被開啟(start)后,本地線程也隨之創(chuàng)建,當它終止(terminated)時,本地線程回收。操作系統(tǒng)負責調(diào)度所有的線程以及派發(fā)可用的cpu資源。java線程的優(yōu)先級以及操作系統(tǒng)線程的優(yōu)先級機制非常復雜,在不同的操作系統(tǒng)中表現(xiàn)也差異極大,此處略。

線程創(chuàng)建和銷毀

有兩種辦地可以向虛擬機中引入一個線程:執(zhí)行java.lang.Thread對象的start方法;或使用JNI將一個已存在的本地線程綁定到vm。出于一些目的,vm內(nèi)部也有一些辦法創(chuàng)建線程,本處不予討論。
在vm的一個線程上實際上關(guān)聯(lián)了若干個對象(HotSpot虛擬機是由面向?qū)ο蟮腸++實現(xiàn)),具體有:
a.java.lang.Thread實例表現(xiàn)java代碼中的一個線程。
b.JavaThread實例表示vm中的一個java.lang.Thread,JavaThread是Thread的子類,它包含額外的用以追蹤線程狀態(tài)的信息。一個JavaThread實例持有關(guān)聯(lián)的java.lang.Thread對象的引用(指針),同時持有OSThread實例的引用。java.lang.Thread也持有JavaThread的引用(以一個整數(shù)表示)。
c.OSThread(直譯為操作系統(tǒng)線程)實例表示了一個操作系統(tǒng)的線程,它包含額外的可用于追蹤線程狀態(tài)的操作系統(tǒng)級別的信息。OSThread包含了一個平臺指定的可用于定位真實操作系統(tǒng)線程的句柄。
當java.lang.Thread實例啟動,vm創(chuàng)建關(guān)聯(lián)的JavaThread和OSThread對象,并最終創(chuàng)建了一個本地線程。在準備好所有vm狀態(tài)后(如線程本地存儲,分配緩存,同步對象等)之后,本地線程得以啟動。本地線程完成初始化并執(zhí)行一個start-up方法,它會導向java.lang.Thread對象的run方法。隨后,在該方法返回或拋出未捕獲的異常時終止線程,并且在終止時與vm交互,這個過程是用以判斷是否它此時也需要終止vm。線程終止會釋放掉所有關(guān)聯(lián)的資源,從已知線程集中移除掉JavaThread實例,執(zhí)行OSThread實例和JavaThread實例的銷毀過程,并最終停止startup 方法的執(zhí)行。
可使用JNI調(diào)用AttachCurrentThread把本地線程綁定到虛擬機。作為此方法的響應,OSThread和JavaThread實例會被創(chuàng)建并進行基本的初始化。接下來會使用綁定線程命令提供的參數(shù)及Thread類的構(gòu)造器反射初始化一個java線程。綁定完成后,線程可以通過可用的JNI方法調(diào)用所需的java代碼。當本地線程不希望繼續(xù)在vm中進行執(zhí)行時,可使用JNI調(diào)用DetachCurrentThread來解除與vm的關(guān)聯(lián)(會釋放資源,丟棄指向java.lang.Thread實例的引用,銷毀JavaThread和OSThread對象等)。
使用JNI調(diào)用CreateJavaVM創(chuàng)建vm是一個特殊的綁定本地線程的例子,它會由執(zhí)行器(java.c)完成或通過一個本地應用來完成。這件事會造成一系列的初始化操作,也會在接下來出現(xiàn)類似執(zhí)行AttachCurrentThread的行為。隨后線程繼續(xù)執(zhí)行所需的java代碼(此例中即反射執(zhí)行main方法)

線程狀態(tài)

vm維護了一組內(nèi)部的線程狀態(tài)來標識各線程的工作。協(xié)調(diào)各線程的交互,或當線程執(zhí)行錯誤進行debug時均需要用到這些狀態(tài)標識。當執(zhí)行了不同的動作時,線程的狀態(tài)可以發(fā)生改變,可即時使用這些轉(zhuǎn)換點檢測相應的線程是否具備執(zhí)行將要執(zhí)行的動作的客觀條件,安全點即是一個典型的例子。
以虛擬機的視圖來看,線程有以下幾個狀態(tài):
_thread_new:表示一個新線程處于初始化的過程中。
_thread_in_Java:表示一個線程正在執(zhí)行java代碼。
_thread_in_vm:表示一個線程正在vm內(nèi)部執(zhí)行。
_thread_blocked:表示線程因某些原因阻塞(原因可能是正在獲取一個鎖,等待一個條件,sleep,執(zhí)行阻塞io等)。
出于debug的目的,可能需要一些額外的信息。如對于一些工具,或者用戶需要進行線程棧轉(zhuǎn)儲或棧跡追蹤等操作時,均需要額外的信息,OSThread維護了相應的一些信息,但部分信息現(xiàn)在已經(jīng)不再使用了,在線程轉(zhuǎn)儲時,可報告的狀態(tài)額外包含:
MONITOR_WAIT:表示線程正在等待獲取競態(tài)鎖。
CONDVAR_WAIT:表示線程正在等待vm使用的內(nèi)部條件變量(與java級別的對象無關(guān)聯(lián))。
OBJECT_WAIT:線程執(zhí)行了Object.wait方法。
虛擬機的其他子系統(tǒng)和庫可能維護了自己的狀態(tài)信息,如JVMTI工具,Thread類本身維護的ThreadState等。這些狀態(tài)一般不被其他組件使用。

虛擬機內(nèi)部線程

JAVA的執(zhí)行有著嚴格的步驟,不同于某些腳本語言,java即使運行簡單的Hello World也需要相應的資源準備,因此,對于最簡單的Hello World,也可以發(fā)現(xiàn)系統(tǒng)中其實創(chuàng)建了若干個線程,它們主要是由vm中的線程和有關(guān)代碼庫中使用的線程(包含引用處理器,終結(jié)者線程等)組成。主要的虛擬機線程有以下幾種:
a.vm線程:它是VMThread的單例,負責執(zhí)行虛擬機操作。
b.周期任務(wù)線程:它是在vm內(nèi)部執(zhí)行周期操作的線程,是WatcherThread的實例。
c.GC線程:顧名思義。
d.編譯器線程:負責運行時執(zhí)行字節(jié)碼到本地代碼的編譯。
e.信號派發(fā)線程:負責等待進程信號并派發(fā)給java級別的信號處理方法。
以上所有線程是Thread類的實例,且所有執(zhí)行java代碼的線程均為JavaThread實例。vm內(nèi)部維護了一個Threads_list的數(shù)據(jù)結(jié)構(gòu),它是一個追蹤所有線程的鏈表,在vm內(nèi)部有一個核心的同步鎖Threads_lock,該鎖就用于保護Threads_list。

10.虛擬機操作和安全點

VMThread會監(jiān)測一個VMOperationQueue隊列,該隊列中存放的成員全部為“操作”,等待相應的操作入隊后,它會執(zhí)行相應的操作。這些操作被交給VMThread來執(zhí)行,因為它們需要vm到達安全點才可執(zhí)行。簡單來說,當vm在到達安全點時,所有vm內(nèi)運行的線程均會阻塞,所有在本地代碼中執(zhí)行的線程在安全點期間被禁止返回vm執(zhí)行。這意味著虛擬機操作可以在已知無線程處于正在更改java堆的前提下進行運行,且此時所有的線程處在一個特殊的,不改變java棧的可檢視狀態(tài)。
最著名的虛擬機操作之一即gc,或者更精確一點是很多gc算法中的“stop the world”階段,但也存在很多基于安全點的其他操作,作者在“54個java官方文檔術(shù)語”一文中簡單列舉了這些操作。
很多虛擬機操作是同步阻塞的,請求者會阻塞到操作完成,但也有一些異步并發(fā)的操作,請求者可以和VMThread并行執(zhí)行。
安全點是使用協(xié)作輪詢的機制初始化的。簡單來說,線程會去詢問“我是否要為一個安全點阻塞”。這個詢問機制的實現(xiàn)并不簡單。當發(fā)生線程的狀態(tài)轉(zhuǎn)換時會常見詢問這個問題,但并是所有的狀態(tài)轉(zhuǎn)換都會詢問,如當一個線程離開vm并進入native代碼塊時。當從編譯的代碼返回時,或在循環(huán)迭代的階段,線程也會詢問這個問題。對于執(zhí)行解釋代碼的線程來說是不常詢問的,但在安全點,它也有相應的方案,當請求安全點時,解釋器會切換到一個包含了該詢問的代碼的轉(zhuǎn)發(fā)表,當安全點結(jié)束后,從派發(fā)表切回。一旦請求了安全點,VMThread必須等到所有已知線程均處于安全點-安全狀態(tài),然后才可執(zhí)行虛擬機操作。在安全點期間,使用Threads_lock來block住那些正在運行的線程,虛擬機操作完成后,VMThread釋放該鎖。

11.C++堆管理

除了由JAVA堆管理者和gc維護的JAVA堆以外,HotSpot虛擬機也使用一個c/c++堆(即所謂的分配堆)來存放虛擬機的內(nèi)部對象和數(shù)據(jù)。這些用來管理C++堆操作的類都由一個基類Arena(競技場)派生而來。
Arena和它的子類提供了位于分配/釋放機制頂層的一個快速分配層。每一個Arena在3個全局的塊池(ChunkPools)中進行內(nèi)存塊(Chunk)的分配。不同的塊池滿足不同大小區(qū)間的分配,舉例說明,如果請求分配1k的內(nèi)存,那么會用“small”塊池分配,如果請求分配10k內(nèi)存,則使用“medium”塊池,這樣可以避免內(nèi)存碎片浪費。
Arena系統(tǒng)也提供了比純粹的分配/釋放機制更佳的性能。因為后者可能需要獲取一個操作系統(tǒng)的全局鎖,它會嚴重影響擴展性并傷害系統(tǒng)性能。Arena是一些緩存了指定內(nèi)存數(shù)量的線程本地對象,這樣的設(shè)計使得它可以在分配時使用“快路”分配而不用獲取該全局鎖,對于釋放內(nèi)存的操作,通常情況下Arena不需要獲得鎖。
Arena的兩個子類,ResourceArena應用于線程本地資源管理,HandleArena用于句柄管理,在client和server編譯器中均用到了這兩種arena。

12.JAVA本地接口(JNI)

JNI代表本地程序接口。它允許運行在jvm中的java代碼與使用其他語言(如c/c++)實現(xiàn)的應用或庫進行交互。JNI本地方法可以用來做很多事情,如創(chuàng)建對象,檢視對象,更新對象,調(diào)用java方法,捕獲拋出的異常,加載類和獲取類信息,執(zhí)行運行時類型檢測等。JNI也可以使用Invocation api來啟用jvm中嵌入的任意native應用,通過它,我們可以輕易地讓已有應用可以用java運行而不用去鏈接vm源碼。
但有重要的一點,一旦使用了JNI,便失去了使用java平臺的兩個重要的好處。
第一,依賴jni的java應用不保證能在多平臺上可用,盡管基于java實現(xiàn)的部分是可以跨宿主機環(huán)境的,使用本地程序語言實現(xiàn)的部分仍舊需要重新編譯。
第二,使用java語言編寫的程序是類型安全的,C或者C++則不是。結(jié)果就是使用了JNI的程序員必須額外注意這部分代碼,行為不端的本地方法可能擾亂整個應用,出于這個原因考慮,在執(zhí)行jni功能前,相應使用到j(luò)ni的應用一定要負責它的安全性檢查。
原則上講,應盡可能少地使用本地方法,并做好這部分代碼與java應用的隔離,作者看來,unsafe后門包是一個典型的案例。
在HotSpot虛擬機中,jni方法的實現(xiàn)相對直接,它使用各種vm內(nèi)部原生規(guī)則來執(zhí)行諸如對象創(chuàng)建方法調(diào)用等行為,通常情況,相應的如解釋器等子系統(tǒng)也使用了這些運行時規(guī)則。
可使用命令行選項-Xcheck:jni來幫助debug那些使用了本地方法的應用,該選項會使得JNI調(diào)用時用到一組debug接口。這些接口會更加嚴格地進行JNI調(diào)用的參數(shù)驗證,同時還會做一些額外的內(nèi)部一致性檢查。
HotSpot對于執(zhí)行本地方法的線程進行了額外“照顧”,對于一些vm的工作,比如gc過程中,一部分線程必須保證在安全點阻塞,從而保證java堆在這些敏感過程中不會再次更改。當我們希望把一個安全點上的線程帶入到本地代碼執(zhí)行時,它會被允許進入本地方法,但是禁止從該方法返回java代碼或者執(zhí)行JNI調(diào)用。

13.虛擬機致命故障處理

毫無疑問,提供致命故障的處理對jvm來說是非常之必需的。以oom為例,它是一個典型的致命錯誤。當發(fā)生這類錯誤時,一定要給用戶提供一些合理且友好的方式來理解致命錯誤成因,從而能快速修復問題,這方面的問題不僅包含應用本身,也包含jvm本身。
第一,一般當jvm在致命故障發(fā)生時crash掉,它會轉(zhuǎn)儲一個hotspot的錯誤日志文件,格式為:hs_err_pid.log。從JDK6開始大幅提升了這些致命錯誤的可診斷性,當發(fā)生crash,錯誤日志文件中會包含當前的內(nèi)存圖像,因此可以很容易搞清楚發(fā)生crash時的內(nèi)存布局。
第二,也可以使用-XX:ErrorFile=選項來指定錯誤日志的位置。
第三,發(fā)生oom時,也會觸發(fā)生成該錯誤文件。
還有一個重要的功能,可以指定一個選項:-XX:OnError="cmd1 args...;com2 ...",這樣當發(fā)生了crash時會執(zhí)行這些指令,相應的指令就比較自由,比如我們可以指定此時執(zhí)行一些諸如dbx或Windbg之類的debugger執(zhí)行相應的操作。早于jdk6的應用可使用-XX:+ShowMessageBoxOnError來指定發(fā)生crash時使用的debugger。以下是jvm內(nèi)部處理致命錯誤的一些摘要:
首先,用VMError類聚合和轉(zhuǎn)儲hs_err_pid.log文件,當發(fā)現(xiàn)未識別的信號/異常時,由操作系統(tǒng)指定的代碼調(diào)用它生成該文件。
第二,vm使用信號來進行內(nèi)部的交流,當出現(xiàn)未識別的信號,致命錯誤處理器被執(zhí)行。而這個信號可能源自一個應用的jni代碼,操作系統(tǒng)本地庫,jre本地庫,甚至是jvm本身。
第三,致命錯誤處理器是慎重編寫的,這也是為了避免它自己也出現(xiàn)錯誤,比如在出現(xiàn)StackOverFlow時,或在持有重要的鎖期間發(fā)生crash(如持有分配鎖)。
死鎖是一種常見的錯誤,一般發(fā)生在應用程序在申請多個鎖時順序不正確的情況。當死鎖發(fā)生時,找出相應的點也是比較困難的,此時可以抓出java進程id,發(fā)送SIGQUIT到該進程(Solaris/Linux),會在標準輸出中輸出java級別的棧信息,這對分析死鎖幫助極大,不過在jdk6之上的版本,已經(jīng)可以使用Jconsole來輕松處理該問題。
順便簡單提一提除了Jconsole/VisualVM等集成工具之外,一些單一目的的自帶工具。
jps:jvm進程工具,可以查看各jvm進程,名稱和編號。
jstat:虛擬機統(tǒng)計信息。比如發(fā)生了多少次full gc等。
jinfo:java配置信息工具,運行時查看jvm進程的配置。
jmap:內(nèi)存映像工具,可以將當前內(nèi)存情況轉(zhuǎn)儲一個快照文件。
jhat:堆轉(zhuǎn)儲快照分析工具。
jstack:java堆棧跟蹤工具。

總結(jié)

本文簡述了包含運行時參數(shù)處理,線程管理,類加載,類數(shù)據(jù)共享,運行時編譯,異常處理,重大錯誤處理等java運行時技術(shù)。參考資料主要源自官方的若干文檔,一部分資料是專屬性的,如專門描述JVM或JIT,但根本無法確定成作于哪個版本(關(guān)于cds作者判斷與JAVA8中的描述相同,但顯然早已不適用),一部分資料是依托于較新版本的,因為新舊版本的文檔并未保持同一目錄結(jié)構(gòu),有些組件未能在新版中找到詳盡的文檔,因此難免會有不準確或過時的內(nèi)容,作者爭取在后面找到最新且更加權(quán)威的資料以修正。
作者個人認為有兩點重要的收獲,一是宏觀上了解了官方出品的HotSpot虛擬機在運行時的框架設(shè)計,理解java在運行時為我們竭盡全力做了哪些事;二是了解某些具體模塊在新版中的優(yōu)化和取舍,從而間接了解接下來java的使用趨勢。如“云友好”,“多適應”,“開放”等。
這三點是作者個人不成熟的簡單總結(jié),寫到這里,也順便對這三點進行一個“簡單總結(jié)”。
云友好其實體現(xiàn)的方面很多,cds就是重要一點,它在最新幾個版本的更新用一句俗化表示:幫用戶省錢。G1定時釋放無用內(nèi)存的新特性也體現(xiàn)了這一點。
多適應和開放也很好理解,不止是gc方面,前面簡單提過的zgc等針對超大堆的gc,以及G1這種放權(quán)讓用戶指定目標的gc,綜合此前的各種gc,基本涵蓋了我們所有可能的應用環(huán)境。同樣的,模塊化系統(tǒng)也天然匹配了中小型到大型項目的需求,一個項目從初創(chuàng)到逐漸壯大,或許最終就是模塊不斷擴充的過程,模塊化系統(tǒng)甚至允許對jdk本身進行按需定制,對于小型設(shè)備用戶也無益于是一個福音。JIT本身就具備自適應的編譯思想,最優(yōu)化最常執(zhí)行的代碼,graal是新出的基于java的JIT編譯器。同步機制也引入了“自適應”自旋鎖,G1中對cs的選擇也具備自適應性等。
再一次,膜拜前輩。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74922.html

相關(guān)文章

  • Java對象結(jié)構(gòu)及大小計算

    摘要:由于的自動內(nèi)存管理系統(tǒng)要求對象起始地址必須是字節(jié)的整數(shù)倍,換句話說,就是對象的大小必須是字節(jié)的整數(shù)倍。對象大小計算要點在位系統(tǒng)下,存放指針的空間大小是字節(jié),是字節(jié),對象頭為字節(jié)。靜態(tài)屬性不算在對象大小內(nèi)。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級算法 GC策略的評價指標 JVM信息查看 GC通用日志解讀 jvm的card table數(shù)據(jù)結(jié)構(gòu) Ja...

    winterdawn 評論0 收藏0
  • Java HotSpot VM中的JIT編譯

    摘要:如同其它虛擬機,虛擬機為字節(jié)碼提供了一個運行時環(huán)境。編譯是一個混合模式的虛擬機,也就是說它既可以解釋字節(jié)碼,又可以將代碼編譯為本地機器碼以更快的執(zhí)行。解決此問題一般是在進程啟動后,對代碼進行預熱以使它們被強制編譯。 Java HotSpot虛擬機是Oracle收購Sun時獲得的,JVM和開源的OpenJDK都是以此虛擬機為基礎(chǔ)發(fā)展的。如同其它虛擬機,HotSpot虛擬機為字節(jié)碼提供了一...

    caozhijian 評論0 收藏0
  • 學習JVM必看書籍

    學習JVM的相關(guān)資料 《深入理解Java虛擬機——JVM高級特性與最佳實踐(第2版)》 showImg(https://segmentfault.com/img/bVbsqF5?w=200&h=200); 基于最新JDK1.7,圍繞內(nèi)存管理、執(zhí)行子系統(tǒng)、程序編譯與優(yōu)化、高效并發(fā)等核心主題對JVM進行全面而深入的分析,深刻揭示JVM的工作原理。以實踐為導向,通過大量與實際生產(chǎn)環(huán)境相結(jié)合的案例展示了解...

    shaonbean 評論0 收藏0
  • Java GC

    摘要:對字節(jié)碼文件進行解釋執(zhí)行,把字節(jié)碼翻譯成相關(guān)平臺上的機器指令。使用命令可對字節(jié)碼文件以及配置文件進行打包可對一個由多個字節(jié)碼文件和配置文件等資源文件構(gòu)成的項目進行打包。和不存在永久代這種說法。 Java技術(shù)體系 從廣義上講,Clojure、JRuby、Groovy等運行于Java虛擬機上的語言及其相關(guān)的程序都屬于Java技術(shù)體系中的一員。如果僅從傳統(tǒng)意義上來看,Sun官方所定義的Jav...

    justCoding 評論0 收藏0
  • JVM詳解1.Java內(nèi)存模型

    摘要:編譯參見深入理解虛擬機節(jié)走進之一自己編譯源碼內(nèi)存模型運行時數(shù)據(jù)區(qū)域根據(jù)虛擬機規(guī)范的規(guī)定,的內(nèi)存包括以下幾個運運行時數(shù)據(jù)區(qū)域程序計數(shù)器程序計數(shù)器是一塊較小的內(nèi)存空間,他可以看作是當前線程所執(zhí)行的字節(jié)碼的行號指示器。 點擊進入我的博客 1.1 基礎(chǔ)知識 1.1.1 一些基本概念 JDK(Java Development Kit):Java語言、Java虛擬機、Java API類庫JRE(...

    TANKING 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<