java虛擬機(jī)學(xué)習(xí)高級(jí)篇
還是繼續(xù)說一下java虛擬機(jī),為什么呢?因?yàn)槲译S意翻著別人的博客一不小心看到有關(guān)jvm的一點(diǎn)新的東西,挺有趣的,就按照我的理解分享一下;
還記得以前學(xué)過一首詩(shī),“看成嶺側(cè)成峰,遠(yuǎn)近高低各不同”,這一句詩(shī)的內(nèi)在含義有的時(shí)候真的會(huì)讓你猛然驚醒,進(jìn)而如獲至寶!的確,有的時(shí)候換一個(gè)角度看問題,你會(huì)發(fā)現(xiàn)不一樣的世界。
我們平常學(xué)java的時(shí)候肯定涉及到了進(jìn)程,多線程的概念,但是有沒有想過操作系統(tǒng)也有進(jìn)程和線程的概念,兩者有關(guān)系嗎?假如我們視角放高一點(diǎn),以操作系統(tǒng)的角度看看一個(gè)java程序的運(yùn)行,又會(huì)是什么樣子的呢?jvm在將字節(jié)碼文件翻譯成機(jī)器碼之后怎么會(huì)調(diào)用cpu呢?自己調(diào)用的還是假借了誰的手呢?jvm在操作系統(tǒng)中到底扮演著一個(gè)什么角色呢?還有最基本的一個(gè)問題,操作系統(tǒng)是什么?
下面我們就來把這些東西整個(gè)的給串一下,當(dāng)然,具體的細(xì)節(jié)還要每個(gè)人自己去研究;
1.簡(jiǎn)單看看操作系統(tǒng)
水平有限,不可能對(duì)操作系統(tǒng)理解得那么透徹,只是說說我自己的理解吧!
一說起操作系統(tǒng)大家肯定既陌生又賊熟悉,為什么呢?因?yàn)槲覀兘?jīng)常使用操作系統(tǒng),比如windows系列,unix系列,macos等等,我們每天一打開電腦首先就會(huì)自動(dòng)啟動(dòng)一個(gè)操作系統(tǒng),但是又有幾個(gè)人真的能了解透徹操作系統(tǒng)呢?我們總是在操作系統(tǒng)中用著幾個(gè)最常見的應(yīng)用,qq、LOL、淘寶、360、優(yōu)酷等等軟件,然而我們卻很少關(guān)注操作系統(tǒng)到底能干什么,假如沒有操作系統(tǒng)會(huì)怎么樣?
我們現(xiàn)在用的所有計(jì)算機(jī)結(jié)構(gòu)都是由馮•諾依曼計(jì)算機(jī)演變過來的,下面我們簡(jiǎn)單看看馮諾依曼計(jì)算機(jī)結(jié)構(gòu):
我們可以看看,我們使用電腦基本就是用這幾部分,有這幾部分足夠我們運(yùn)行一個(gè)程序了?。∧敲?,我們需要操作系統(tǒng)干嘛呢?
首先我們要考慮到一點(diǎn),由于運(yùn)算器是能識(shí)別二進(jìn)制碼,所以我們?cè)谳斎朐O(shè)備中只能輸入很多很多的0和1組成的代碼,這樣的代碼簡(jiǎn)直就是一股泥石流,讓人絕望,然而早期的計(jì)算機(jī)還就是這樣編程的,通過我們?nèi)斯さ姆绞綄懞芏嗟?和1去對(duì)那些屏幕、cpu等硬件的驅(qū)動(dòng)程序(比如聲音驅(qū)動(dòng)、顯卡驅(qū)動(dòng),再驅(qū)動(dòng)程序再底層其實(shí)就是用高低電位去使得硬件發(fā)生變化)發(fā)出指令進(jìn)行操作;這個(gè)時(shí)候可沒有什么線程什么的,沒有cpu等待時(shí)間什么的,可想而知效率感人!
在操作系統(tǒng)沒有出來的時(shí)候編程真的很痛苦,一般人真干不了!過了很多年之后,終于有了操作系統(tǒng),操作系統(tǒng)本質(zhì)上是一個(gè)軟件,我們可以簡(jiǎn)單的把操作系統(tǒng)看作是對(duì)這些驅(qū)動(dòng)的封裝,在操作系統(tǒng)內(nèi)部(可以叫做內(nèi)核)提供了兩類接口,一類是只要那些硬件驅(qū)動(dòng)程序?qū)崿F(xiàn)了這些接口,操作系統(tǒng)就通過這些接口使用那些硬件設(shè)備;然后另一類接口就是給我們程序員去實(shí)現(xiàn)的,我們只需要面向這些接口編程我們就可以間接的操作硬件!
而我們熟悉的JVM就是實(shí)現(xiàn)了操作系統(tǒng)提供的給程序員實(shí)現(xiàn)的那一類接口,而JVM又向JAVA程序員提供了一些java api,我們只需要按照java api就能間接的通過jvm對(duì)操作系統(tǒng)進(jìn)行控制,進(jìn)而對(duì)驅(qū)動(dòng)發(fā)指令,最后就是可以對(duì)那些硬件中的數(shù)據(jù)進(jìn)行處理;
于是我們可以看看下面這個(gè)圖(暫時(shí)忽略UNIX命令和庫(kù)),這個(gè)圖中用戶編寫的應(yīng)用程序可以看作是JVM,JVM可以根據(jù)操作系統(tǒng)提供的系統(tǒng)調(diào)用接口對(duì)操作系統(tǒng)內(nèi)核發(fā)出一些指令,內(nèi)核就會(huì)調(diào)用硬件的驅(qū)動(dòng)程序?qū)τ布M(jìn)行一些操作,比如讀取文件數(shù)據(jù)、向文件中寫入數(shù)據(jù)等等
由于操作系統(tǒng)提供了這么強(qiáng)大的功能,于是我們現(xiàn)在幾乎所有的編程語(yǔ)言都是在操作系統(tǒng)的基礎(chǔ)上進(jìn)行編程,然后通過解釋器這類東西將我們寫的程序轉(zhuǎn)化為計(jì)算機(jī)能夠識(shí)別的機(jī)器碼,然后調(diào)用操作系統(tǒng)接口,最終實(shí)現(xiàn)對(duì)硬件的操作。
順便一提,這里用戶編寫的應(yīng)用程序我們就可以看作是QQ,優(yōu)酷,酷狗音樂等等軟件,如果是單核cpu,你同時(shí)打開多個(gè)應(yīng)用,那么cpu會(huì)來回在多個(gè)應(yīng)用切換,只是由于切換時(shí)間太短,我們感覺不到,還以為是同時(shí)運(yùn)行的!就好像電影,其實(shí)電影是一張張圖每隔零點(diǎn)幾秒進(jìn)行翻頁(yè),但是我們眼睛察覺不出來,還有我們鼠標(biāo)移動(dòng)在屏幕上移動(dòng),為什么這么流暢呢?因?yàn)槲覀兊钠聊幻扛袅泓c(diǎn)幾秒就刷新一次,當(dāng)然我們察覺不出來。
現(xiàn)在我們看看操作系統(tǒng)的定義:是一組控制和管理計(jì)算機(jī)硬件和軟件資源,合理對(duì)各類作業(yè)進(jìn)行調(diào)度,以及方便用戶的程序的集合”,是不是有點(diǎn)懂了,我們繼續(xù)往下看!
2.操作系統(tǒng)的內(nèi)存結(jié)構(gòu)
我們可以簡(jiǎn)單看看操作系統(tǒng)的內(nèi)存結(jié)構(gòu),一般查資料我們看到的是下圖這樣的,我們肯定看不懂!瑪?shù)?,這都是什么鬼,除了那個(gè)棧和堆其他的都不知道干嘛的!
于是我們可以稍作修改,去掉一些不利于我們理解的東西,如下圖所示,是不是就熟悉了一點(diǎn)了,貌似跟jvm的內(nèi)存結(jié)構(gòu)很像?。。?!
我們把硬盤和操作系統(tǒng)的PC寄存器添加上去試試效果,下圖所示!有沒有似曾相識(shí)的感覺,對(duì)的,jvm跟這個(gè)幾乎一模一樣,這里的硬盤其實(shí)就是相當(dāng)于jvm的方法區(qū)(方法區(qū)也叫做永久代,看名字就知道是可以永久保存的?。。?,還有jvm中的本地方法棧不在jvm的內(nèi)存結(jié)構(gòu)中,原來在這里啊,其實(shí)指的就是操作系統(tǒng)的棧;
操作系統(tǒng)的PC寄存器和jvm的PC寄存器用處差不多,是記錄當(dāng)前CPU運(yùn)行命令的地址;
3.jvm在操作系統(tǒng)中的角色
舉個(gè)簡(jiǎn)單的例子,當(dāng)我們?cè)陔娔X桌面上雙擊了QQ,那么QQ這個(gè)應(yīng)用就會(huì)啟動(dòng),操作系統(tǒng)就會(huì)創(chuàng)建一個(gè)進(jìn)程,然后會(huì)在操作系統(tǒng)的堆中為這個(gè)進(jìn)程開辟空間,用于存放該進(jìn)程中產(chǎn)生的數(shù)據(jù);
對(duì)操作系統(tǒng)來說,JVM和這個(gè)QQ應(yīng)用沒有什么兩樣,一視同仁,于是我們可以得到這樣的一個(gè)圖:
最后我們?cè)侔裫vm中的內(nèi)存結(jié)構(gòu)完善一下,如下圖:
看到這里,我們不禁的笑了,原來jvm就是按照操作系統(tǒng)的樣子做了一遍,假如我們站在操作系統(tǒng)角度看,jvm其實(shí)就是一個(gè)平常的應(yīng)用;假如我們站在java字節(jié)碼文件的角度看,其實(shí)jvm就相當(dāng)于一個(gè)操作系統(tǒng),把字節(jié)碼文件放進(jìn)了jvm這個(gè)虛擬操作系統(tǒng)的硬盤中(方法區(qū))中,然后jvm中對(duì)方法區(qū)中的數(shù)據(jù)進(jìn)行讀取、創(chuàng)建對(duì)象等后續(xù)各種操作;
操作系統(tǒng)棧和jvm?;疽粯?;操作系統(tǒng)堆和jvm堆基本也一樣,但是釋放內(nèi)存的方式有點(diǎn)差異,操作系統(tǒng)的堆是要程序員手動(dòng)釋放,而jvm的堆是靠gc自動(dòng)清理;
現(xiàn)在我們?cè)侔俣纫幌驴纯磈vm的定義,現(xiàn)在應(yīng)該知道jvm是個(gè)什么東西了吧!哈哈
4.方法區(qū)中的信息
雖然我們之前簡(jiǎn)要的說了方法區(qū)中的數(shù)據(jù)就是有關(guān)于類的基本信息,靜態(tài)變量和常量池,但是說得比較籠統(tǒng);
現(xiàn)在我們?cè)倩仡^看看jvm方法區(qū)中存的具體是什么信息:
(1)類信息
(2)字段信息
(3)方法信息
(4)常量池
(5)靜態(tài)變量
(6)一個(gè)到Class對(duì)象的引用
(7)一個(gè)到加載該類的類加載器的引用
(8)方法表(有的JVM有,有的JVM沒有):這是一個(gè)數(shù)組,保存了該類在堆中創(chuàng)建的所有對(duì)象的實(shí)例方法的引用,但是比較耗內(nèi)存,所以有的jvm設(shè)計(jì)者沒有設(shè)計(jì)這個(gè)方法表;
對(duì)應(yīng)的java代碼如下:
public final class com.wyq.test.ClassStruct extends Object implements Serializable {//1.類信息:包括訪問修飾符,全類名,父類名,實(shí)現(xiàn)接口等 //2.對(duì)象字段信息:包括訪問修飾符,類型,字段名 private String name; private int id; //4.常量池:常量和一些符號(hào)引用 public final int CONST_INT=0; public final String CONST_STR="CONST_STR"; //5.靜態(tài)變量 public static String static_str="static_str"; //3.方法信息:包括修飾符,方法返回值,方法名,局部變量,方法體(花括號(hào)中的內(nèi)容) public static final String getStatic_str (){ return ClassStruct.static_str; }}
可以清楚的看到其實(shí)方法區(qū)中保存的就是將字節(jié)碼文件中的所有信息!
5.java程序執(zhí)行流程
現(xiàn)在我們結(jié)合操作系統(tǒng)的知識(shí)和上圖看看我們運(yùn)行一個(gè)java程序,電腦中到底是干了什么?就不考慮緩存了。。。
1.我們?cè)贓clpse中寫完一個(gè)java源程序,必須要Ctrl+S保存一下,這個(gè)動(dòng)作就是將源程序保存到電腦硬盤中;
2.然后我們運(yùn)行一個(gè)main方法,這個(gè)時(shí)候編譯器就會(huì)將硬盤中的源程序讀取到內(nèi)存并編譯成字節(jié)碼文件(注意這個(gè)字節(jié)碼文件不一定非要是本計(jì)算機(jī)編譯的,只要是符合字節(jié)碼文件格式的字節(jié)碼文件都行,別人電腦傳給你的肯定可以;這符合java一處編譯到處運(yùn)行的原則)
3.我們運(yùn)行一個(gè)main方法的同時(shí),對(duì)操作系統(tǒng)來說就是新創(chuàng)建了一個(gè)進(jìn)程,就要在操作系統(tǒng)的堆中申請(qǐng)這個(gè)進(jìn)程的內(nèi)存空間,我們把這塊內(nèi)存空間稱為JVM(注意,假如運(yùn)行兩個(gè)java程序那么這里就會(huì)創(chuàng)建兩個(gè)jvm的內(nèi)存空間)
4.jvm實(shí)例創(chuàng)建成功,就會(huì)在這個(gè)實(shí)例之中的內(nèi)存空間進(jìn)行分配,java棧,java堆,方法區(qū)等
5.由于類裝載子系統(tǒng),會(huì)把類加載器先加載到j(luò)ava堆中,然后類加載器根據(jù)我們的java源程序類名去指定路徑中去加載字節(jié)碼文件(雙親委托機(jī)制),放入方法區(qū)中
6.由執(zhí)行引擎去執(zhí)行這個(gè)字節(jié)碼文件,伴隨著驗(yàn)證、準(zhǔn)備、解析和初始化,最終在java堆中生成了Class對(duì)象和實(shí)例化對(duì)象。
7.假如調(diào)用本地方法(就是Native修飾的方法)就會(huì)涉及到本地方法棧(和java棧作用差不多,壓棧和彈棧),在操作系統(tǒng)棧中壓入棧幀,假如在本地方法中還調(diào)用java中的方法,這個(gè)時(shí)候在操作系統(tǒng)的棧中壓入一個(gè)棧幀,然后下一個(gè)棧幀卻到了jvm的棧中,很有趣的一個(gè)東西。
8.我們方法調(diào)用完畢,棧中棧幀彈出,棧清理完畢;然后gc會(huì)對(duì)java堆中對(duì)象進(jìn)行回收釋放內(nèi)存空間,然后gc還會(huì)對(duì)方法區(qū)進(jìn)行清理,自此jvm中的內(nèi)存空間清理完畢;
9 操作系統(tǒng)堆jvm進(jìn)行清理,jvm進(jìn)程結(jié)束。
總結(jié):
由于對(duì)操作系統(tǒng)的理解還處于比較懵懂的狀態(tài),所以文中可能會(huì)有很多詞語(yǔ)運(yùn)用不當(dāng),很慚愧,以后肯定會(huì)抽個(gè)時(shí)間慢操作系統(tǒng)整個(gè)的學(xué)習(xí)一遍的,希望這一天不要來的太遲,哈哈哈
相關(guān)文章
Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的文件上傳案例示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的文件上傳案例,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07Spring?Boot?集成JWT實(shí)現(xiàn)前后端認(rèn)證的示例代碼
小程序、H5應(yīng)用的快速發(fā)展,使得前后端分離已經(jīng)成為了趨勢(shì),本文主要介紹了Spring?Boot?集成JWT實(shí)現(xiàn)前后端認(rèn)證,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04idea運(yùn)行java的配置詳細(xì)教程(包含maven,mysql下載配置)
程序員們?cè)陂_發(fā)的時(shí)候,一定會(huì)用到Intellij?IDEA這個(gè)集成開發(fā)環(huán)境,這篇文章主要給大家介紹了關(guān)于idea運(yùn)行java的配置(包含maven,mysql下載配置)的相關(guān)資料,需要的朋友可以參考下2024-05-05springboot中如何通過cors協(xié)議解決跨域問題
這篇文章主要介紹了springboot中通過cors協(xié)議解決跨域問題,cors是一個(gè)w3c標(biāo)準(zhǔn),它允許瀏覽器(目前ie8以下還不能被支持)像我們不同源的服務(wù)器發(fā)出xmlHttpRequest請(qǐng)求,我們可以繼續(xù)使用ajax進(jìn)行請(qǐng)求訪問。具體內(nèi)容詳情大家跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05java項(xiàng)目中的絕對(duì)路徑和相對(duì)路徑用法說明
這篇文章主要介紹了java項(xiàng)目中的絕對(duì)路徑和相對(duì)路徑用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08深入分析Android系統(tǒng)中SparseArray的源碼
這篇文章主要介紹了深入分析Android系統(tǒng)中SparseArray的源碼,SparseArray為Java實(shí)現(xiàn),需要的朋友可以參考下2015-07-07Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能
這篇文章主要介紹了Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06