一徐鹤、JVM與Dalvik
二垃环、JVM
JVM 全稱 Java Virtual Machine,也就是我們耳熟能詳?shù)?Java 虛擬機(jī)返敬。它能識(shí)別 .class后綴的文件遂庄,并且能夠解析它的指令,最終調(diào)用操作系統(tǒng)上的函數(shù)劲赠,完成我們想要的操作涛目。
1.Java 程序不一樣,使用 javac 編譯成 .class 文件之后凛澎,還需要使用 Java 命令去主動(dòng)執(zhí)行它霹肝,操作系統(tǒng)并不認(rèn)識(shí)這些 .class 文件。所以JVM就是一個(gè)翻譯预厌;
2阿迈、跨平臺(tái):我們寫(xiě)的這個(gè)類Person這個(gè)類,在不同的操作系統(tǒng)上(Linux轧叽、Windows苗沧、MacOS 等平臺(tái))執(zhí)行刊棕,效果是一樣,這個(gè)就是JVM的跨平臺(tái)性待逞。為了實(shí)現(xiàn)跨平臺(tái)型甥角,不同操作系統(tǒng)有不同的JDK的版本。
跨語(yǔ)言:JVM只識(shí)別字節(jié)碼识樱,所以JVM其實(shí)跟語(yǔ)言是解耦的嗤无,也就是沒(méi)有直接關(guān)聯(lián),并不是它翻譯Java文件怜庸,而是識(shí)別class文件当犯,這個(gè)一般稱之為字節(jié)碼。還有像Groovy 割疾、Kotlin嚎卫、Jruby等等語(yǔ)言,它們其實(shí)也是編譯成字節(jié)碼宏榕,所以也可以在JVM上面跑拓诸,這個(gè)就是JVM的跨語(yǔ)言特征。
JVM只是一個(gè)翻譯麻昼,把Class翻譯成機(jī)器識(shí)別的代碼奠支,但是需要注意,JVM 不會(huì)自己生成代碼抚芦,需要大家編寫(xiě)代碼倍谜,同時(shí)需要很多依賴類庫(kù),這個(gè)時(shí)候就需要用到JRE燕垃。
JRE是什么枢劝,它除了包含JVM之外,提供了很多的類庫(kù)(就是我們說(shuō)的jar包卜壕,它可以提供一些即插即用的功能您旁,比如讀取或者操作文件,連接網(wǎng)絡(luò)轴捎,使用I/O等等之類的)這些東西就是JRE提供的基礎(chǔ)類庫(kù)鹤盒。JVM 標(biāo)準(zhǔn)加上實(shí)現(xiàn)的一大堆基礎(chǔ)類庫(kù),就組成了 Java 的運(yùn)行時(shí)環(huán)境侦副,也就是我們常說(shuō)的 JRE(Java Runtime Environment)侦锯。
但對(duì)于程序員來(lái)說(shuō),JRE還不夠秦驯。我寫(xiě)完要編譯代碼尺碰,還需要調(diào)試代碼,還需要打包代碼、有時(shí)候還需要反編譯代碼亲桥。所以我們會(huì)使用JDK洛心,因?yàn)镴DK還提供了一些非常好用的小工具,比如 javac(編譯代碼)题篷、java词身、jar (打包代碼)、javap(反編譯<反匯編>)等番枚。這個(gè)就是JDK法严。
一個(gè) Java 程序,首先經(jīng)過(guò) javac 編譯成 .class 文件葫笼,然后 JVM 將其加載到方法區(qū)深啤,執(zhí)行引擎將會(huì)執(zhí)行這些字節(jié)碼。執(zhí)行時(shí)渔欢,會(huì)翻譯成操作系統(tǒng)相關(guān)的函數(shù)墓塌。JVM 作為 .class 文件的翻譯存在,輸入字節(jié)碼奥额,調(diào)用操作系統(tǒng)函數(shù)。
過(guò)程如下:Java 文件->編譯器>字節(jié)碼->JVM->機(jī)器碼访诱。
Java 引以為豪的就是它的自動(dòng)內(nèi)存管理機(jī)制垫挨。相比于 C++的手動(dòng)內(nèi)存管理、復(fù)雜難以理解的指針等触菜,Java 程序?qū)懫饋?lái)就方便的多九榔。
在 Java 中,JVM 內(nèi)存主要分為堆涡相、程序計(jì)數(shù)器哲泊、方法區(qū)、虛擬機(jī)棧和本地方法棧
1催蝗、程序計(jì)數(shù)器
較小的內(nèi)存空間切威,當(dāng)前線程執(zhí)行的字節(jié)碼的行號(hào)指示器;各線程之間獨(dú)立存儲(chǔ)丙号,互不影響先朦。
程序計(jì)數(shù)器是一塊很小的內(nèi)存空間,主要用來(lái)記錄各個(gè)線程執(zhí)行的字節(jié)碼的地址犬缨,例如喳魏,分支、循環(huán)怀薛、跳轉(zhuǎn)刺彩、異常、線程恢復(fù)等都依賴于計(jì)數(shù)器。
由于 Java 是多線程語(yǔ)言创倔,當(dāng)執(zhí)行的線程數(shù)量超過(guò) CPU 核數(shù)時(shí)三热,線程之間會(huì)根據(jù)時(shí)間片輪詢爭(zhēng)奪 CPU 資源。如果一個(gè)線程的時(shí)間片用完了三幻,或者是其它原因?qū)е逻@個(gè)線程的 CPU 資源被提前搶奪就漾,那么這個(gè)退出的線程就需要單獨(dú)的一個(gè)程序計(jì)數(shù)器,來(lái)記錄下一條運(yùn)行的指令念搬。
程序計(jì)數(shù)器也是JVM中唯一不會(huì)OOM(OutOfMemory)的內(nèi)存區(qū)域
2抑堡、虛擬機(jī)棧
棧是什么樣的數(shù)據(jù)結(jié)構(gòu)?先進(jìn)后出(FILO)的數(shù)據(jù)結(jié)構(gòu)朗徊,
虛擬機(jī)棧在JVM運(yùn)行過(guò)程中存儲(chǔ)當(dāng)前線程運(yùn)行方法所需的數(shù)據(jù)首妖,指令、返回地址爷恳。
Java 虛擬機(jī)棧是基于線程的有缆。哪怕你只有一個(gè) main() 方法,也是以線程的方式運(yùn)行的温亲。在線程的生命周期中棚壁,參與計(jì)算的數(shù)據(jù)會(huì)頻繁地入棧和出棧,棧的生命周期是和線程一樣的栈虚。
棧里的每條數(shù)據(jù)袖外,就是棧幀。在每個(gè) Java 方法被調(diào)用的時(shí)候魂务,都會(huì)創(chuàng)建一個(gè)棧幀曼验,并入棧。一旦完成相應(yīng)的調(diào)用粘姜,則出棧鬓照。所有的棧幀都出棧后,線程也就結(jié)束了孤紧。
每個(gè)棧幀豺裆,都包含四個(gè)區(qū)域:(局部變量表、操作數(shù)棧坛芽、動(dòng)態(tài)連接留储、返回地址)
棧的大小缺省為1M,可用參數(shù) –Xss調(diào)整大小咙轩,例如-Xss256k
局部變量表:顧名思義就是局部變量的表获讳,用于存放我們的局部變量的。首先它是一個(gè)32位的長(zhǎng)度活喊,主要存放我們的Java的八大基礎(chǔ)數(shù)據(jù)類型丐膝,一般32位就可以存放下,如果是64位的就使用高低位占用兩個(gè)也可以存放下,如果是局部的一些對(duì)象帅矗,比如我們的Object對(duì)象偎肃,我們只需要存放它的一個(gè)引用地址即可。
操作數(shù)據(jù)棧:存放我們方法執(zhí)行的操作數(shù)的浑此,它就是一個(gè)棧累颂,先進(jìn)后出的棧結(jié)構(gòu),操作數(shù)棧凛俱,就是用來(lái)操作的紊馏,操作的的元素可以是任意的java數(shù)據(jù)類型,所以我們知道一個(gè)方法剛剛開(kāi)始的時(shí)候蒲犬,這個(gè)方法的操作數(shù)棧就是空的朱监,操作數(shù)棧運(yùn)行方法就是JVM一直運(yùn)行入棧/出棧的操作
動(dòng)態(tài)連接:Java語(yǔ)言特性多態(tài)(需要類運(yùn)行時(shí)才能確定具體的方法)。
返回地址:正常返回(調(diào)用程序計(jì)數(shù)器中的地址作為返回)原叮、異常的話(通過(guò)異常處理器表<非棧幀中的>來(lái)確定)
3赫编、本地方法棧
本地方法棧跟 Java 虛擬機(jī)棧的功能類似,Java 虛擬機(jī)棧用于管理 Java 函數(shù)的調(diào)用奋隶,而本地方法棧則用于管理本地方法的調(diào)用擂送。但本地方法并不是用 Java 實(shí)現(xiàn)的,而是由 C 語(yǔ)言實(shí)現(xiàn)的达布。
4团甲、堆
堆是 JVM 上最大的內(nèi)存區(qū)域,我們申請(qǐng)的幾乎所有的對(duì)象黍聂,都是在這里存儲(chǔ)的。我們常說(shuō)的垃圾回收身腻,操作的對(duì)象就是堆产还。
堆空間一般是程序啟動(dòng)時(shí),就申請(qǐng)了嘀趟,但是并不一定會(huì)全部使用脐区。
隨著對(duì)象的頻繁創(chuàng)建,堆空間占用的越來(lái)越多她按,就需要不定期的對(duì)不再使用的對(duì)象進(jìn)行回收牛隅。這個(gè)在 Java 中,就叫作 GC(Garbage Collection)酌泰。
那一個(gè)對(duì)象創(chuàng)建的時(shí)候媒佣,到底是在堆上分配,還是在棧上分配呢陵刹?這和兩個(gè)方面有關(guān):對(duì)象的類型和在 Java 類中存在的位置默伍。
Java 的對(duì)象可以分為基本數(shù)據(jù)類型和普通對(duì)象。
對(duì)于普通對(duì)象來(lái)說(shuō),JVM 會(huì)首先在堆上創(chuàng)建對(duì)象也糊,然后在其他地方使用的其實(shí)是它的引用炼蹦。比如,把這個(gè)引用保存在虛擬機(jī)棧的局部變量表中狸剃。
對(duì)于基本數(shù)據(jù)類型來(lái)說(shuō)(byte掐隐、short、int钞馁、long虑省、float、double指攒、char)慷妙,有兩種情況。當(dāng)你在方法體內(nèi)聲明了基本數(shù)據(jù)類型的對(duì)象允悦,它就會(huì)在棧上直接分配膝擂。其他情況,都是在堆上分配隙弛。
深入辨析堆和棧
1.功能
以棧幀的方式存儲(chǔ)方法調(diào)用的過(guò)程架馋,并存儲(chǔ)方法調(diào)用過(guò)程中基本數(shù)據(jù)類型的變量(int、short全闷、long叉寂、byte、float总珠、double屏鳍、boolean、char等)以及對(duì)象的引用變量局服,其內(nèi)存分配在棧上钓瞭,變量出了作用域就會(huì)自動(dòng)釋放;
而堆內(nèi)存用來(lái)存儲(chǔ)Java中的對(duì)象淫奔。無(wú)論是成員變量山涡,局部變量,還是類變量唆迁,它們指向的對(duì)象都存儲(chǔ)在堆內(nèi)存中鸭丛;
2.線程獨(dú)享還是共享
棧內(nèi)存歸屬于單個(gè)線程,每個(gè)線程都會(huì)有一個(gè)棧內(nèi)存唐责,其存儲(chǔ)的變量只能在其所屬線程中可見(jiàn)鳞溉,即棧內(nèi)存可以理解成線程的私有內(nèi)存。
堆內(nèi)存中的對(duì)象對(duì)所有線程可見(jiàn)妒蔚。堆內(nèi)存中的對(duì)象可以被所有線程訪問(wèn)穿挨。
3.空間大小
棧的內(nèi)存要遠(yuǎn)遠(yuǎn)小于堆內(nèi)存