其實(shí)語(yǔ)言好會(huì)多半取決他的解釋器和編譯器,如同一個(gè)好的翻譯,在不失原本意作的基礎(chǔ)上,又添磚加瓦。Java 之所以有今天的成功和地位多半是因?yàn)榈?JVM (java 虛擬機(jī))。

看一看我們開(kāi)發(fā)的代碼最終是如何運(yùn)行在計(jì)算機(jī)上的。首先我們編寫(xiě)好的 java 文件通過(guò) javac 命令編譯成為 class 文件,class 字節(jié)碼文件,是 java 專有可以運(yùn)行在 JVM 上的文件。這一點(diǎn)與 c 或 c++ 直接編譯為可以計(jì)算機(jī)上運(yùn)行文件不同。編譯后被驗(yàn)證可以運(yùn)行在 JVM 上代碼會(huì)被執(zhí)行引擎解釋為其機(jī)器語(yǔ)言運(yùn)行在計(jì)算機(jī)上。


簡(jiǎn)單用一句話描述一下整個(gè)流程,我們編譯好的class文件,會(huì)先被加載到內(nèi)存中,然后由引擎解釋運(yùn)行在系統(tǒng)上。JVM 實(shí)現(xiàn)了 hotspot 架構(gòu)。


1.?加載?-?將編譯好的字節(jié)碼寫(xiě)入(分配)到內(nèi)存。
引導(dǎo)類加載器:負(fù)責(zé)加載的?java?的核心類,例如位于?jre?中。
?擴(kuò)展類加載:負(fù)責(zé)加載 Java 的擴(kuò)展庫(kù)。Java 虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫(kù)目錄。該類加載器在此目錄里面查找并加載 Java 類
1.2?應(yīng)用類加載,通過(guò)?-cp?指定類路徑?的類路徑(CLASSPATH)來(lái)加載?Java?類。一般來(lái)說(shuō),Java?應(yīng)用的類都是由它來(lái)完成加載的
2.?鏈接
驗(yàn)證:根據(jù)?JVM?的標(biāo)準(zhǔn)來(lái)檢查,驗(yàn)證加載的字節(jié)碼是否能夠被執(zhí)行。
準(zhǔn)備:可能翻譯成就緒也不錯(cuò),在這個(gè)階段,會(huì)分配內(nèi)存給一些靜態(tài)類,和類級(jí)別的變量,而不是對(duì)象,這是內(nèi)存分配。例如這里有一個(gè)靜態(tài)布爾型的變量在編碼階段賦值為?true,而這里準(zhǔn)備階段為其分配內(nèi)存并將其賦值為默認(rèn)值false,而在初始化階段將其賦值為編碼中?true。把虛擬機(jī)常量池中的符號(hào)引用轉(zhuǎn)換為直接引用,我們?cè)诰幾g過(guò)程中經(jīng)常遇到?java.lang.ClassNotFoundException?就是在這里拋出的,如果A?類引用了?B?類,在這里沒(méi)有找到?B?類被加載就會(huì)拋出?ClassNotFoundException?異常。
初始化:如果這個(gè)類還沒(méi)有被加載和鏈接,那先進(jìn)行加載和鏈接。假如這個(gè)類存在直接父類,并且這個(gè)類還沒(méi)有被初始化(注意:在一個(gè)類加載器中,類只能初始化一次),那就初始化直接的父類(不適用于接口)。如果類中存在static標(biāo)識(shí)的塊,那就依次執(zhí)行這些初始化語(yǔ)句。

方法區(qū):這里保存的信息(數(shù)據(jù))是類的元數(shù)據(jù),什么是元數(shù)據(jù)呢?元數(shù)據(jù)就是說(shuō)明類一些屬性例如,他位置,是私有的還是公有的。這部分內(nèi)存為?JVM?所有的線程共用的區(qū)域,默認(rèn)分配給這個(gè)區(qū)域?64M。如果我們應(yīng)用中有成千上萬(wàn)的類,默認(rèn)分配給方法區(qū)的內(nèi)存可能就不夠,就會(huì)拋出 java.lang.OutOfMemoryError:PermGen:space 異常。這時(shí)可以通過(guò)命令分配給方法區(qū)更多的內(nèi)存。

堆:堆在 java 中是一種通用性的內(nèi)存池,用于存放創(chuàng)建 Java 對(duì)象。只要 new 機(jī)會(huì)在這里創(chuàng)建一個(gè)對(duì)象,無(wú)需釋放,這里是由垃圾回收機(jī)制管理釋放內(nèi)存。
堆內(nèi)存和方法區(qū),是在創(chuàng)建 JVM 實(shí)例化就創(chuàng)建的內(nèi)存區(qū)域,他們與線程無(wú)關(guān),所以先將他們兩單拿出來(lái)分為一類。