java虛擬機學習高級篇
還是繼續(xù)說一下java虛擬機,為什么呢?因為我隨意翻著別人的博客一不小心看到有關jvm的一點新的東西,挺有趣的,就按照我的理解分享一下;
還記得以前學過一首詩,“看成嶺側成峰,遠近高低各不同”,這一句詩的內在含義有的時候真的會讓你猛然驚醒,進而如獲至寶!的確,有的時候換一個角度看問題,你會發(fā)現不一樣的世界。
我們平常學java的時候肯定涉及到了進程,多線程的概念,但是有沒有想過操作系統(tǒng)也有進程和線程的概念,兩者有關系嗎?假如我們視角放高一點,以操作系統(tǒng)的角度看看一個java程序的運行,又會是什么樣子的呢?jvm在將字節(jié)碼文件翻譯成機器碼之后怎么會調用cpu呢?自己調用的還是假借了誰的手呢?jvm在操作系統(tǒng)中到底扮演著一個什么角色呢?還有最基本的一個問題,操作系統(tǒng)是什么?
下面我們就來把這些東西整個的給串一下,當然,具體的細節(jié)還要每個人自己去研究;
1.簡單看看操作系統(tǒng)
水平有限,不可能對操作系統(tǒng)理解得那么透徹,只是說說我自己的理解吧!
一說起操作系統(tǒng)大家肯定既陌生又賊熟悉,為什么呢?因為我們經常使用操作系統(tǒng),比如windows系列,unix系列,macos等等,我們每天一打開電腦首先就會自動啟動一個操作系統(tǒng),但是又有幾個人真的能了解透徹操作系統(tǒng)呢?我們總是在操作系統(tǒng)中用著幾個最常見的應用,qq、LOL、淘寶、360、優(yōu)酷等等軟件,然而我們卻很少關注操作系統(tǒng)到底能干什么,假如沒有操作系統(tǒng)會怎么樣?
我們現在用的所有計算機結構都是由馮•諾依曼計算機演變過來的,下面我們簡單看看馮諾依曼計算機結構:

我們可以看看,我們使用電腦基本就是用這幾部分,有這幾部分足夠我們運行一個程序了?。∧敲?,我們需要操作系統(tǒng)干嘛呢?
首先我們要考慮到一點,由于運算器是能識別二進制碼,所以我們在輸入設備中只能輸入很多很多的0和1組成的代碼,這樣的代碼簡直就是一股泥石流,讓人絕望,然而早期的計算機還就是這樣編程的,通過我們人工的方式寫很多的0和1去對那些屏幕、cpu等硬件的驅動程序(比如聲音驅動、顯卡驅動,再驅動程序再底層其實就是用高低電位去使得硬件發(fā)生變化)發(fā)出指令進行操作;這個時候可沒有什么線程什么的,沒有cpu等待時間什么的,可想而知效率感人!
在操作系統(tǒng)沒有出來的時候編程真的很痛苦,一般人真干不了!過了很多年之后,終于有了操作系統(tǒng),操作系統(tǒng)本質上是一個軟件,我們可以簡單的把操作系統(tǒng)看作是對這些驅動的封裝,在操作系統(tǒng)內部(可以叫做內核)提供了兩類接口,一類是只要那些硬件驅動程序實現了這些接口,操作系統(tǒng)就通過這些接口使用那些硬件設備;然后另一類接口就是給我們程序員去實現的,我們只需要面向這些接口編程我們就可以間接的操作硬件!
而我們熟悉的JVM就是實現了操作系統(tǒng)提供的給程序員實現的那一類接口,而JVM又向JAVA程序員提供了一些java api,我們只需要按照java api就能間接的通過jvm對操作系統(tǒng)進行控制,進而對驅動發(fā)指令,最后就是可以對那些硬件中的數據進行處理;
于是我們可以看看下面這個圖(暫時忽略UNIX命令和庫),這個圖中用戶編寫的應用程序可以看作是JVM,JVM可以根據操作系統(tǒng)提供的系統(tǒng)調用接口對操作系統(tǒng)內核發(fā)出一些指令,內核就會調用硬件的驅動程序對硬件進行一些操作,比如讀取文件數據、向文件中寫入數據等等

由于操作系統(tǒng)提供了這么強大的功能,于是我們現在幾乎所有的編程語言都是在操作系統(tǒng)的基礎上進行編程,然后通過解釋器這類東西將我們寫的程序轉化為計算機能夠識別的機器碼,然后調用操作系統(tǒng)接口,最終實現對硬件的操作。
順便一提,這里用戶編寫的應用程序我們就可以看作是QQ,優(yōu)酷,酷狗音樂等等軟件,如果是單核cpu,你同時打開多個應用,那么cpu會來回在多個應用切換,只是由于切換時間太短,我們感覺不到,還以為是同時運行的!就好像電影,其實電影是一張張圖每隔零點幾秒進行翻頁,但是我們眼睛察覺不出來,還有我們鼠標移動在屏幕上移動,為什么這么流暢呢?因為我們的屏幕每隔零點幾秒就刷新一次,當然我們察覺不出來。
現在我們看看操作系統(tǒng)的定義:是一組控制和管理計算機硬件和軟件資源,合理對各類作業(yè)進行調度,以及方便用戶的程序的集合”,是不是有點懂了,我們繼續(xù)往下看!
2.操作系統(tǒng)的內存結構
我們可以簡單看看操作系統(tǒng)的內存結構,一般查資料我們看到的是下圖這樣的,我們肯定看不懂!瑪德,這都是什么鬼,除了那個棧和堆其他的都不知道干嘛的!

于是我們可以稍作修改,去掉一些不利于我們理解的東西,如下圖所示,是不是就熟悉了一點了,貌似跟jvm的內存結構很像?。。?!

我們把硬盤和操作系統(tǒng)的PC寄存器添加上去試試效果,下圖所示!有沒有似曾相識的感覺,對的,jvm跟這個幾乎一模一樣,這里的硬盤其實就是相當于jvm的方法區(qū)(方法區(qū)也叫做永久代,看名字就知道是可以永久保存的?。。?,還有jvm中的本地方法棧不在jvm的內存結構中,原來在這里啊,其實指的就是操作系統(tǒng)的棧;
操作系統(tǒng)的PC寄存器和jvm的PC寄存器用處差不多,是記錄當前CPU運行命令的地址;

3.jvm在操作系統(tǒng)中的角色
舉個簡單的例子,當我們在電腦桌面上雙擊了QQ,那么QQ這個應用就會啟動,操作系統(tǒng)就會創(chuàng)建一個進程,然后會在操作系統(tǒng)的堆中為這個進程開辟空間,用于存放該進程中產生的數據;
對操作系統(tǒng)來說,JVM和這個QQ應用沒有什么兩樣,一視同仁,于是我們可以得到這樣的一個圖:

最后我們再把jvm中的內存結構完善一下,如下圖:

看到這里,我們不禁的笑了,原來jvm就是按照操作系統(tǒng)的樣子做了一遍,假如我們站在操作系統(tǒng)角度看,jvm其實就是一個平常的應用;假如我們站在java字節(jié)碼文件的角度看,其實jvm就相當于一個操作系統(tǒng),把字節(jié)碼文件放進了jvm這個虛擬操作系統(tǒng)的硬盤中(方法區(qū))中,然后jvm中對方法區(qū)中的數據進行讀取、創(chuàng)建對象等后續(xù)各種操作;
操作系統(tǒng)棧和jvm?;疽粯?;操作系統(tǒng)堆和jvm堆基本也一樣,但是釋放內存的方式有點差異,操作系統(tǒng)的堆是要程序員手動釋放,而jvm的堆是靠gc自動清理;
現在我們再百度一下看看jvm的定義,現在應該知道jvm是個什么東西了吧!哈哈

4.方法區(qū)中的信息
雖然我們之前簡要的說了方法區(qū)中的數據就是有關于類的基本信息,靜態(tài)變量和常量池,但是說得比較籠統(tǒng);
現在我們再回頭看看jvm方法區(qū)中存的具體是什么信息:
(1)類信息
(2)字段信息
(3)方法信息
(4)常量池
(5)靜態(tài)變量
(6)一個到Class對象的引用
(7)一個到加載該類的類加載器的引用
(8)方法表(有的JVM有,有的JVM沒有):這是一個數組,保存了該類在堆中創(chuàng)建的所有對象的實例方法的引用,但是比較耗內存,所以有的jvm設計者沒有設計這個方法表;
對應的java代碼如下:
public final class com.wyq.test.ClassStruct extends Object implements Serializable {//1.類信息:包括訪問修飾符,全類名,父類名,實現接口等
//2.對象字段信息:包括訪問修飾符,類型,字段名
private String name;
private int id;
//4.常量池:常量和一些符號引用
public final int CONST_INT=0;
public final String CONST_STR="CONST_STR";
//5.靜態(tài)變量
public static String static_str="static_str";
//3.方法信息:包括修飾符,方法返回值,方法名,局部變量,方法體(花括號中的內容)
public static final String getStatic_str (){
return ClassStruct.static_str;
}}
可以清楚的看到其實方法區(qū)中保存的就是將字節(jié)碼文件中的所有信息!
5.java程序執(zhí)行流程

現在我們結合操作系統(tǒng)的知識和上圖看看我們運行一個java程序,電腦中到底是干了什么?就不考慮緩存了。。。
1.我們在Eclpse中寫完一個java源程序,必須要Ctrl+S保存一下,這個動作就是將源程序保存到電腦硬盤中;
2.然后我們運行一個main方法,這個時候編譯器就會將硬盤中的源程序讀取到內存并編譯成字節(jié)碼文件(注意這個字節(jié)碼文件不一定非要是本計算機編譯的,只要是符合字節(jié)碼文件格式的字節(jié)碼文件都行,別人電腦傳給你的肯定可以;這符合java一處編譯到處運行的原則)
3.我們運行一個main方法的同時,對操作系統(tǒng)來說就是新創(chuàng)建了一個進程,就要在操作系統(tǒng)的堆中申請這個進程的內存空間,我們把這塊內存空間稱為JVM(注意,假如運行兩個java程序那么這里就會創(chuàng)建兩個jvm的內存空間)
4.jvm實例創(chuàng)建成功,就會在這個實例之中的內存空間進行分配,java棧,java堆,方法區(qū)等
5.由于類裝載子系統(tǒng),會把類加載器先加載到java堆中,然后類加載器根據我們的java源程序類名去指定路徑中去加載字節(jié)碼文件(雙親委托機制),放入方法區(qū)中
6.由執(zhí)行引擎去執(zhí)行這個字節(jié)碼文件,伴隨著驗證、準備、解析和初始化,最終在java堆中生成了Class對象和實例化對象。
7.假如調用本地方法(就是Native修飾的方法)就會涉及到本地方法棧(和java棧作用差不多,壓棧和彈棧),在操作系統(tǒng)棧中壓入棧幀,假如在本地方法中還調用java中的方法,這個時候在操作系統(tǒng)的棧中壓入一個棧幀,然后下一個棧幀卻到了jvm的棧中,很有趣的一個東西。
8.我們方法調用完畢,棧中棧幀彈出,棧清理完畢;然后gc會對java堆中對象進行回收釋放內存空間,然后gc還會對方法區(qū)進行清理,自此jvm中的內存空間清理完畢;
9 操作系統(tǒng)堆jvm進行清理,jvm進程結束。
總結:
由于對操作系統(tǒng)的理解還處于比較懵懂的狀態(tài),所以文中可能會有很多詞語運用不當,很慚愧,以后肯定會抽個時間慢操作系統(tǒng)整個的學習一遍的,希望這一天不要來的太遲,哈哈哈
相關文章
Java中的字節(jié)流InputStream和OutputStream詳解
這篇文章主要介紹了Java中的字節(jié)流InputStream和OutputStream詳解,繼承自InputStream的流都是用于向程序中輸入數據,且數據的單位為字節(jié)8bit,我們看到的具體的某一些管道,凡是以InputStream結尾的管道,都是以字節(jié)的形式向我們的程序輸入數據,需要的朋友可以參考下2023-10-10
elasticsearch節(jié)點間通信的基礎transport啟動過程
這篇文章主要為大家介紹了elasticsearch節(jié)點間通信的基礎transport啟動過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04

