Java跨平臺(tái)根本原因荣德,JVM內(nèi)存分布

Java與C++之間有一堵由內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”,墻外面的人想進(jìn)去童芹,墻里面的人想出來(lái)涮瞻。

什么是跨平臺(tái)?

我之前一直在想一個(gè)問(wèn)題假褪,一直在說(shuō)Java可以跨平臺(tái)署咽,但是C代碼可以放到 windows 平臺(tái)執(zhí)行也可以放到 linux 平臺(tái)里面執(zhí)行,為什么不說(shuō)C語(yǔ)言跨平臺(tái)呢生音?

跨平臺(tái)定義

跨平臺(tái)定義:首先這是基于源碼的跨平臺(tái)宁否。也就是說(shuō),只寫(xiě)一套代碼缀遍,但是在各個(gè)平臺(tái)如 Windows慕匠,Linux,unix 都能順利運(yùn)行域醇,這就是跨平臺(tái)台谊。

我們知道 Java 是運(yùn)行在虛擬機(jī)里面的蓉媳,不管你的服務(wù)器是windows系統(tǒng)還是linux系統(tǒng),只要在各個(gè)平臺(tái)上面安裝 java虛擬機(jī)锅铅,那么就可以愉快的運(yùn)行Java代碼酪呻,所以說(shuō)Java是平臺(tái)無(wú)關(guān)的語(yǔ)言即可跨平臺(tái)。

然而 C&C++ 語(yǔ)言盐须,他們是平臺(tái)有關(guān)的語(yǔ)言玩荠,我們?cè)?Windows 系統(tǒng)下面編寫(xiě)好了代碼,運(yùn)行的很快樂(lè)贼邓,但是拿到 Linux 系統(tǒng)運(yùn)行卻不一定能成功可能報(bào)錯(cuò)阶冈。例如一段打開(kāi)文件的代碼實(shí)現(xiàn),我隨便百度一下兩個(gè)平臺(tái)的代碼實(shí)現(xiàn)如下圖立帖,分為 windows 實(shí)現(xiàn)和 linux 實(shí)現(xiàn),也就是說(shuō)C語(yǔ)言平臺(tái)有關(guān)悠砚,不能跨平臺(tái)運(yùn)行晓勇。

C語(yǔ)言代碼能不能跨平臺(tái)運(yùn)行呢?

當(dāng)然可以了灌旧,有兩種方法可以讓C程序代碼實(shí)現(xiàn)跨平臺(tái)運(yùn)行绑咱,

寫(xiě)好兼容代碼。

只要你在代碼里面寫(xiě)好兼容代碼枢泰,同時(shí)寫(xiě)一套 windows 的實(shí)現(xiàn)描融,再寫(xiě)一套 linux 的實(shí)現(xiàn),那么這套代碼就可以同時(shí)在 windows 和 linux 系統(tǒng)下執(zhí)行衡蚂。假如一個(gè)打開(kāi)文件的操作在windows和linux里面的實(shí)現(xiàn)不一樣窿克,偽代碼如下:

if (當(dāng)前系統(tǒng) == windows){

? open1 file ;

} else if (當(dāng)前系統(tǒng) == linux) {

? open2 file ;

} else {

? open3 file ;

}

用了洪荒之力寫(xiě)完這篇,有收獲的朋友點(diǎn)個(gè)在看或者分享鼓勵(lì)一下吧毛甲,十分感謝~

公眾號(hào)下篇內(nèi)容預(yù)告:對(duì)象的創(chuàng)建年叮,內(nèi)存布局以及訪(fǎng)問(wèn)定位

這樣看,整個(gè)類(lèi)信息主要是拆開(kāi)來(lái)玻募,為了方便管理只损,存儲(chǔ)和調(diào)度從而劃分成了這幾個(gè)區(qū)域。

本地方法棧由于是調(diào)用的c代碼七咧,是通過(guò)動(dòng)態(tài)鏈接的方式而不是傳統(tǒng)數(shù)據(jù)結(jié)構(gòu)中的棧結(jié)構(gòu)跃惫,所以也抽出來(lái)進(jìn)行特殊處理,劃分一個(gè)本地方法棧艾栋。

程序計(jì)數(shù)器方便調(diào)度字節(jié)碼指令爆存,讓程序動(dòng)起來(lái),即代碼按照代碼順序執(zhí)行蝗砾;

堆區(qū)存放對(duì)象终蒂,對(duì)象變化比較大蜂林,涉及到垃圾回收因此也單獨(dú)劃分區(qū)域來(lái)存儲(chǔ),便于管理和回收拇泣;

棧是存放活的動(dòng)起來(lái)的東西噪叙,專(zhuān)門(mén)對(duì)類(lèi)函數(shù)方法來(lái)設(shè)置的,入棧出棧也不用垃圾回收內(nèi)存什么的(試想一下霉翔,類(lèi)里面的方法很多睁蕾,而且是方法進(jìn)入方法返回,都有進(jìn)有出债朵,在數(shù)據(jù)結(jié)構(gòu)里面只有棧這種結(jié)構(gòu)能滿(mǎn)足設(shè)計(jì)子眶,棧幀當(dāng)然就是根據(jù)方法里面的局部變量什么的來(lái)設(shè)計(jì)的);

方法區(qū)其實(shí)就是存放一些“死的東西”序芦,不會(huì)動(dòng)的東西臭杰,例如類(lèi)的一些死的信息(類(lèi)名,常量等)谚中;

從設(shè)計(jì)者角度根據(jù)類(lèi)的內(nèi)容來(lái)劃分JVM內(nèi)存:

JVM堆渴杆,棧,方法區(qū)對(duì)應(yīng)結(jié)構(gòu)

這部分內(nèi)容是具備動(dòng)態(tài)性的宪塔,運(yùn)行期間可以放入新的常量磁奖,例如String類(lèi)的intern()方法,以及new String(“123”)的時(shí)候某筐,String類(lèi)型先會(huì)先去常量池看123存在不比搭,存在的話(huà)直接在堆區(qū)生成對(duì)象并且引用他,如果不存在會(huì)先去常量池創(chuàng)建一個(gè)“123”再去堆引用指向他南誊。

運(yùn)行時(shí)常量池是方法區(qū)的一部分身诺,class文件除了類(lèi)的版本,字段抄囚,方法戚长,接口等描述信息之外,還有一項(xiàng)信息是常量池怠苔,用于存放編譯期生成的各種字面量和符號(hào)引用同廉,這些內(nèi)容將在類(lèi)加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。

運(yùn)行時(shí)常量池

這個(gè)說(shuō)法是建立在HotPot虛擬機(jī)的柑司,其設(shè)計(jì)團(tuán)隊(duì)把GC分代收集擴(kuò)展到了方法區(qū)迫肖,也即是用永久代來(lái)實(shí)現(xiàn)方法區(qū)以至于垃圾收集器可以像管理堆一樣管理方法區(qū)內(nèi)存,對(duì)其他的虛擬機(jī)來(lái)說(shuō)是不存在永久代的概念的攒驰。

很多人都把方法區(qū)稱(chēng)為永久代蟆湖,本質(zhì)上兩者不等價(jià)。

傳說(shuō)中的永久代

已被虛擬機(jī)加載的類(lèi)信息

常量

靜態(tài)變量

即時(shí)編譯器(JIT)編譯后的代碼

用于存儲(chǔ):

由圖看出此部分主要有靜態(tài)的常量(類(lèi)信息不會(huì)變)玻粪,和運(yùn)行時(shí)常量池隅津。

方法區(qū)

StackOverFlowError:棧溢出诬垂,線(xiàn)程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度(代碼實(shí)現(xiàn)可以寫(xiě)一個(gè)遞歸方法,然后不給遞歸出口伦仍,調(diào)用遞歸方法每次遞歸都會(huì)產(chǎn)生新的棧幀直到把棧區(qū)打滿(mǎn)溢出)

OutOfMemoryError:虛擬機(jī)棧擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠內(nèi)存(代碼實(shí)現(xiàn)可以一直循環(huán)new對(duì)象结窘,直到把堆區(qū)打滿(mǎn)內(nèi)存溢出)

異常:

Navtive 方法是 Java 通過(guò) JNI 直接調(diào)用本地 C/C++ 庫(kù),可以認(rèn)為是 Native 方法相當(dāng)于 C/C++ 暴露給 Java 的一個(gè)接口充蓝,Java 通過(guò)調(diào)用這個(gè)接口從而調(diào)用到 C/C++ 方法隧枫。當(dāng)線(xiàn)程調(diào)用 Java 方法時(shí),虛擬機(jī)會(huì)創(chuàng)建一個(gè)棧幀并壓入 Java 虛擬機(jī)棧谓苟。然而當(dāng)它調(diào)用的是 native 方法時(shí)官脓,虛擬機(jī)會(huì)保持 Java 虛擬機(jī)棧不變,也不會(huì)向 Java 虛擬機(jī)棧中壓入新的棧幀涝焙,虛擬機(jī)只是簡(jiǎn)單地動(dòng)態(tài)連接并直接調(diào)用指定的 native 方法卑笨。

這里簡(jiǎn)單提一下,他與Java虛擬機(jī)棧所發(fā)揮的作用是非常相似的仑撞,其區(qū)別不過(guò)是虛擬機(jī)棧為虛擬機(jī)執(zhí)行 Java 方法(也就是字節(jié)碼)服務(wù)赤兴,而本地方法棧則是為虛擬機(jī)使用到的 Native 方法服務(wù)。

本地方法棧

局部變量表的內(nèi)存空間在編譯期間就完成了分配派草,進(jìn)入一個(gè)方法的時(shí)候搀缠,這個(gè)方法需要在幀里面分配多少局部變量空間是確定的铛楣,不會(huì)改變近迁。

存放了編譯期可知的各種基本數(shù)據(jù)類(lèi)型(boolean,byte纯露,char媳荒,short跳夭,int...)

存放對(duì)象引用(注意不是對(duì)象本身,是引用搏存,即指針)

存放字節(jié)碼指令地址 returnAddress 類(lèi)型(即方法返回地址,方法出口)

說(shuō)白了就是:

局部變量表

操作數(shù)棧

動(dòng)態(tài)鏈接

棧幀包括的內(nèi)容:

每一個(gè)方法從調(diào)用開(kāi)始到完成的過(guò)程矢洲,就是一個(gè)棧幀在在虛擬機(jī)中入棧到出棧的過(guò)程

棧區(qū)

Java堆是被所有線(xiàn)程共享的一塊內(nèi)存區(qū)域璧眠,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建;

此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例读虏,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存责静;

OutOfMemoryError異常。如果在堆中沒(méi)有內(nèi)存完成實(shí)例分配盖桥,并且堆也無(wú)法再擴(kuò)展時(shí)會(huì)拋出此異常灾螃。

對(duì)于大多數(shù)應(yīng)用來(lái)說(shuō),Java堆(Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊揩徊,主要記住三點(diǎn):

Java堆是垃圾收集器管理的主要區(qū)域腰鬼,因此很多時(shí)候也被稱(chēng)作“GC堆”嵌赠,幸好國(guó)內(nèi)沒(méi)翻譯成“垃圾堆”。

java堆(Heap)

注:程序計(jì)數(shù)器是唯一一個(gè)在JVM規(guī)范中沒(méi)有規(guī)定任何 OutOfMemoryError 的區(qū)域熄赡。

ps:這個(gè)地方我解釋這么清楚是因?yàn)槠渌拇蠹叶勢(shì)^多姜挺,很容易理解,但是這個(gè)區(qū)域至少我大學(xué)的時(shí)候這個(gè)地方就不是很理解本谜。

我們都知道在計(jì)算機(jī)里面CPU是從指令寄存器拿到執(zhí)行指令進(jìn)行工作初家,當(dāng)指令寄存器里面一條指令被CPU拿走執(zhí)行,那么寄存器就會(huì)把程序計(jì)數(shù)器里面指定的下一個(gè)需要執(zhí)行的字節(jié)碼指令對(duì)應(yīng)的CPU指令拿進(jìn)來(lái)乌助,讓CPU進(jìn)行執(zhí)行溜在,所以實(shí)現(xiàn)字節(jié)碼指令都可以做到有序執(zhí)行,需要注意的是程序計(jì)數(shù)器存放的都是下一個(gè)字節(jié)碼指令的地址他托,這樣才可以一直往下執(zhí)行嘛掖肋。

放上一篇文章的圖,這是 jvm 的一些指令赏参,最終會(huì)和計(jì)算機(jī)的相關(guān)指令相對(duì)應(yīng)志笼。

程序計(jì)數(shù)器存放的是下一條字節(jié)碼指令執(zhí)行的地址,存放地址的地方把篓,因此只需要一塊較小的內(nèi)存空間纫溃,它的作用是當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)碼行號(hào)指示器。

程序計(jì)數(shù)器

線(xiàn)程私有:Java虛擬機(jī)的多線(xiàn)程實(shí)現(xiàn)韧掩,是通過(guò)線(xiàn)程輪流切換并分配處理器執(zhí)行時(shí)間的方式來(lái)實(shí)現(xiàn)的紊浩,再任何一個(gè)確定的時(shí)刻,一個(gè)處理器都只會(huì)執(zhí)行一條線(xiàn)程中的指令疗锐。因此為了線(xiàn)程切換之后能恢復(fù)到正確的的執(zhí)行位置坊谁,每條線(xiàn)程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,各個(gè)線(xiàn)程之間計(jì)數(shù)器不影響?yīng)毩⒋鎯?chǔ)滑臊,我們稱(chēng)這類(lèi)內(nèi)存區(qū)域?yàn)椤熬€(xiàn)程私有”內(nèi)存口芍。

方法區(qū)和堆區(qū)是所有線(xiàn)程共享的,棧和程序計(jì)數(shù)器都是線(xiàn)程私有各自管各自的雇卷。

運(yùn)行時(shí)數(shù)據(jù)區(qū)總覽

運(yùn)行之前的解析讀者可以直接看我前面寫(xiě)的幾篇鬓椭,本文主要想說(shuō)的是運(yùn)行時(shí)數(shù)據(jù)區(qū)。

Java在 JVM 中的運(yùn)行生命周期和類(lèi)加載詳細(xì)過(guò)程路徑直達(dá)java類(lèi)在JVM中的生命周期

java 編譯的字節(jié)碼解析路徑直達(dá)從JVM設(shè)計(jì)者的角度來(lái)看.class文件結(jié)構(gòu)关划,一文弄懂.class文件的身份地位

可以看到Java源代碼先是經(jīng)過(guò)編譯器進(jìn)行編譯小染,變成.class文件,由類(lèi)加載器加載進(jìn)內(nèi)存運(yùn)行祭玉。

JVM內(nèi)存結(jié)構(gòu)

這樣看來(lái)JVM內(nèi)存結(jié)構(gòu)是很重要把跤场!

然而C/C++是直接使用物理硬件和操作系統(tǒng)的內(nèi)存模型脱货,因此會(huì)由于不同平臺(tái)的內(nèi)存模型不同而產(chǎn)生差異岛都。

Java虛擬機(jī)定義了一種Java內(nèi)存模型(Java memory model, JMM)來(lái)屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪(fǎng)問(wèn)差異律姨,簡(jiǎn)單理解也就是說(shuō)Java虛擬機(jī)相當(dāng)于是在源碼和平臺(tái)之間抽象了一層出來(lái),專(zhuān)門(mén)處理一些平臺(tái)之間訪(fǎng)問(wèn)的兼容問(wèn)題臼疫,使得源碼可以一次編譯到處運(yùn)行择份。

Java跨平臺(tái)的原因

可以看到編譯器是關(guān)鍵,再拿C語(yǔ)言為例烫堤,Linux下直接使用 gcc編譯器 編譯C程序荣赶,在Windows下使用對(duì)應(yīng)的 mingw 編譯C程序,這樣用兩套不同的編譯器來(lái)在不同的平臺(tái)進(jìn)行編譯鸽斟,不同的編譯器都是封裝了各自平臺(tái)對(duì)C語(yǔ)言的處理拔创,但是這樣也很麻煩啊,所以Java虛擬機(jī)的價(jià)值就更加突顯了富蓄。

它的執(zhí)行過(guò)程是:預(yù)處理->編譯->匯編->鏈接->機(jī)器碼

因?yàn)橹С諧++語(yǔ)言的各個(gè)平臺(tái)的架構(gòu)不同(比如CPU能夠處理的指令集不一樣)剩燥,所以一份C++源代碼要想在另一個(gè)操作系統(tǒng)平臺(tái)上執(zhí)行,就必須用該平臺(tái)相對(duì)應(yīng)的C++代碼編譯器對(duì)C++源代碼重新進(jìn)行編譯立倍,生成該平臺(tái)可以直接執(zhí)行的機(jī)器代碼灭红。

移植編譯器

可以想象,如果你是要實(shí)現(xiàn)一個(gè)大的工程有很多代碼口注,你得寫(xiě)多少兼容代碼变擒,而且測(cè)試的時(shí)候還需要同時(shí)放到兩個(gè)平臺(tái)去測(cè)試,這是多么的夸張寝志,使得程序員原本就不茂密的頭發(fā)更加雪上添霜娇斑。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市澈段,隨后出現(xiàn)的幾起案子悠菜,更是在濱河造成了極大的恐慌舰攒,老刑警劉巖败富,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異摩窃,居然都是意外死亡兽叮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)猾愿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鹦聪,“玉大人,你說(shuō)我怎么就攤上這事蒂秘≡蟊荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵姻僧,是天一觀(guān)的道長(zhǎng)规丽。 經(jīng)常有香客問(wèn)我蒲牧,道長(zhǎng),這世上最難降的妖魔是什么赌莺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任冰抢,我火速辦了婚禮,結(jié)果婚禮上艘狭,老公的妹妹穿的比我還像新娘挎扰。我一直安慰自己,他們只是感情好巢音,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布遵倦。 她就那樣靜靜地躺著,像睡著了一般官撼。 火紅的嫁衣襯著肌膚如雪骇吭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天歧寺,我揣著相機(jī)與錄音燥狰,去河邊找鬼。 笑死斜筐,一個(gè)胖子當(dāng)著我的面吹牛龙致,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播顷链,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼目代,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嗤练?” 一聲冷哼從身側(cè)響起榛了,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎煞抬,沒(méi)想到半個(gè)月后霜大,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡革答,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年战坤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片残拐。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡途茫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溪食,到底是詐尸還是另有隱情囊卜,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站栅组,受9級(jí)特大地震影響袱衷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笑窜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一致燥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧排截,春花似錦嫌蚤、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至认罩,卻和暖如春箱蝠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垦垂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工宦搬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劫拗。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓间校,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親页慷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子憔足,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345