帶着新人看java虛擬機03

  分享一篇博客:https://blog.csdn.net/yfqnihao/article/details/8289363,本篇有部分參考這篇博客!!!java

  仍是繼續說一下java虛擬機,爲何呢?由於我隨意翻着別人的博客一不當心看到有關jvm的一點新的東西,挺有趣的,就按照個人理解分享一下;程序員

  還記得之前學過一首詩,「當作嶺側成峯,遠近高低各不一樣」,這一句詩的內在含義有的時候真的會讓你猛然驚醒,進而如獲至寶!的確,有的時候換一個角度看問題,你會發現不同的世界。macos

  咱們日常學java的時候確定涉及到了進程,多線程的概念,可是有沒有想過操做系統也有進程和線程的概念,二者有關係嗎?假如咱們視角放高一點,以操做系統的角度看看一個java程序的運行,又會是什麼樣子的呢?jvm在將字節碼文件翻譯成機器碼以後怎麼會調用cpu呢?本身調用的仍是假借了誰的手呢?jvm在操做系統中到底扮演着一個什麼角色呢?還有最基本的一個問題,操做系統是什麼?編程

  下面咱們就來把這些東西整個的給串一下,固然,具體的細節還要每一個人本身去研究;windows

 

1.簡單看看操做系統api

  水平有限,不可能對操做系統理解得那麼透徹,只是說說我本身的理解吧!數組

  一提及操做系統你們確定既陌生又賊熟悉,爲何呢?由於咱們常用操做系統,好比windows系列,unix系列,macos等等,咱們天天一打開電腦首先就會自動啓動一個操做系統,可是又有幾我的真的能瞭解透徹操做系統呢?咱們老是在操做系統中用着幾個最多見的應用,qq、LOL、淘寶、360、優酷等等軟件,然而咱們卻不多關注操做系統到底能幹什麼,假如沒有操做系統會怎麼樣?緩存

  咱們如今用的全部計算機結構都是由馮•諾依曼計算機演變過來的,下面咱們簡單看看馮諾依曼計算機結構:多線程

 

  咱們能夠看看,咱們使用電腦基本就是用這幾部分,有這幾部分足夠咱們運行一個程序了啊!那麼,咱們須要操做系統幹嗎呢?jvm

  首先咱們要考慮到一點,因爲運算器是能識別二進制碼,因此咱們在輸入設備中只能輸入不少不少的0和1組成的代碼,這樣的代碼簡直就是一股泥石流,讓人絕望,然而早期的計算機還就是這樣編程的,經過咱們人工的方式寫不少的0和1去對那些屏幕、cpu等硬件的驅動程序(好比聲音驅動、顯卡驅動,再驅動程序再底層其實就是用高低電位去使得硬件發生變化)發出指令進行操做;這個時候可沒有什麼線程什麼的,沒有cpu等待時間什麼的,可想而知效率感人!

  在操做系統沒有出來的時候編程真的很痛苦,通常人真幹不了!過了不少年以後,終於有了操做系統,操做系統本質上是一個軟件,咱們能夠簡單的把操做系統看做是對這些驅動的封裝,在操做系統內部(能夠叫作內核)提供了兩類接口,一類是隻要那些硬件驅動程序實現了這些接口,操做系統就經過這些接口使用那些硬件設備;而後另外一類接口就是給咱們程序員去實現的,咱們只須要面向這些接口編程咱們就能夠間接的操做硬件!

  而咱們熟悉的JVM就是實現了操做系統提供的給程序員實現的那一類接口,而JVM又向JAVA程序員提供了一些java api,咱們只須要按照java api就能間接的經過jvm對操做系統進行控制,進而對驅動發指令,最後就是能夠對那些硬件中的數據進行處理;

  因而咱們能夠看看下面這個圖(暫時忽略UNIX命令和庫),這個圖中用戶編寫的應用程序能夠看做是JVM,JVM能夠根據操做系統提供的系統調用接口對操做系統內核發出一些指令,內核就會調用硬件的驅動程序對硬件進行一些操做,好比讀取文件數據、向文件中寫入數據等等

  因爲操做系統提供了這麼強大的功能,因而咱們如今幾乎全部的編程語言都是在操做系統的基礎上進行編程,而後經過解釋器這類東西將咱們寫的程序轉化爲計算機可以識別的機器碼,而後調用操做系統接口,最終實現對硬件的操做。

  順便一提,這裏用戶編寫的應用程序咱們就能夠看做是QQ,優酷,酷狗音樂等等軟件,若是是單核cpu,你同時打開多個應用,那麼cpu會來回在多個應用切換,只是因爲切換時間過短,咱們感受不到,還覺得是同時運行的!就好像電影,其實電影是一張張圖每隔零點幾秒進行翻頁,可是咱們眼睛察覺不出來,還有咱們鼠標移動在屏幕上移動,爲何這麼流暢呢?由於咱們的屏幕每隔零點幾秒就刷新一次,固然咱們察覺不出來。

   如今咱們看看操做系統的定義:是一組控制和管理計算機硬件和軟件資源,合理對各種做業進行調度,以及方便用戶的程序的集合」,是否是有點懂了,咱們繼續往下看!

 

2.操做系統的內存結構

  咱們能夠簡單看看操做系統的內存結構,通常查資料咱們看到的是下圖這樣的,咱們確定看不懂!瑪德,這都是什麼鬼,除了那個棧和堆其餘的都不知道幹嗎的!

 

  因而咱們能夠稍做修改,去掉一些不利於咱們理解的東西,以下圖所示,是否是就熟悉了一點了,貌似跟jvm的內存結構很像啊!!!

 

   咱們把硬盤和操做系統的PC寄存器添加上去試試效果,下圖所示!有沒有似曾相識的感受,對的,jvm跟這個幾乎如出一轍,這裏的硬盤其實就是至關於jvm的方法區(方法區也叫作永久代,看名字就知道是能夠永久保存的啊!),還有jvm中的本地方法棧不在jvm的內存結構中,原來在這裏啊,其實指的就是操做系統的棧;

  操做系統的PC寄存器和jvm的PC寄存器用處差很少,是記錄當前CPU運行命令的地址;

 

 3.jvm在操做系統中的角色

  舉個簡單的例子,當咱們在電腦桌面上雙擊了QQ,那麼QQ這個應用就會啓動,操做系統就會建立一個進程,而後會在操做系統的堆中爲這個進程開闢空間,用於存放該進程中產生的數據;

  對操做系統來講,JVM和這個QQ應用沒有什麼兩樣,一視同仁,因而咱們能夠獲得這樣的一個圖:

 

  最後咱們再把jvm中的內存結構完善一下,以下圖:

 

  看到這裏,咱們不由的笑了,原來jvm就是按照操做系統的樣子作了一遍,假如咱們站在操做系統角度看,jvm其實就是一個日常的應用;假如咱們站在java字節碼文件的角度看,其實jvm就至關於一個操做系統,把字節碼文件放進了jvm這個虛擬操做系統的硬盤中(方法區)中,而後jvm中對方法區中的數據進行讀取、建立對象等後續各類操做;

  操做系統棧和jvm棧基本同樣;操做系統堆和jvm堆基本也同樣,可是釋放內存的方式有點差別,操做系統的堆是要程序員手動釋放,而jvm的堆是靠gc自動清理;

  如今咱們再百度一下看看jvm的定義,如今應該知道jvm是個什麼東西了吧!哈哈

 

 

4.方法區中的信息

   雖然咱們以前簡要的說了方法區中的數據就是有關於類的基本信息,靜態變量和常量池,可是說得比較籠統;

   如今咱們再回頭看看jvm方法區中存的具體是什麼信息:

  (1)類信息

  (2)字段信息

  (3)方法信息

  (4)常量池

  (5)靜態變量

  (6)一個到Class對象的引用

  (7)一個到加載該類的類加載器的引用

  (8)方法表(有的JVM有,有的JVM沒有):這是一個數組,保存了該類在堆中建立的全部對象的實例方法的引用,可是比較耗內存,因此有的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.靜態變量
   public static String static_str="static_str";
    
 
 //3.方法信息:包括修飾符,方法返回值,方法名,局部變量,方法體(花括號中的內容)
   public static final String getStatic_str (){
      return ClassStruct.static_str;
 }}

  能夠清楚的看到其實方法區中保存的就是將字節碼文件中的全部信息!   

 

5.java程序執行流程

  如今咱們結合操做系統的知識和上圖看看咱們運行一個java程序,電腦中究竟是幹了什麼?就不考慮緩存了。。。

  1.咱們在Eclpse中寫完一個java源程序,必需要Ctrl+S保存一下,這個動做就是將源程序保存到電腦硬盤中;

  2.而後咱們運行一個main方法,這個時候編譯器就會將硬盤中的源程序讀取到內存並編譯成字節碼文件(注意這個字節碼文件不必定非要是本計算機編譯的,只要是符合字節碼文件格式的字節碼文件都行,別人電腦傳給你的確定能夠;這符合java一處編譯處處運行的原則)

  3.咱們運行一個main方法的同時,對操做系統來講就是新建立了一個進程,就要在操做系統的堆中申請這個進程的內存空間,咱們把這塊內存空間稱爲JVM(注意,假如運行兩個java程序那麼這裏就會建立兩個jvm的內存空間)

  4.jvm實例建立成功,就會在這個實例之中的內存空間進行分配,java棧,java堆,方法區等

  5.因爲類裝載子系統,會把類加載器先加載到java堆中,而後類加載器根據咱們的java源程序類名去指定路徑中去加載字節碼文件(雙親委託機制),放入方法區中

  6.由執行引擎去執行這個字節碼文件,伴隨着驗證、準備、解析和初始化,最終在java堆中生成了Class對象和實例化對象。

  7.假如調用本地方法(就是Native修飾的方法)就會涉及到本地方法棧(和java棧做用差很少,壓棧和彈棧),在操做系統棧中壓入棧幀,假如在本地方法中還調用java中的方法,這個時候在操做系統的棧中壓入一個棧幀,而後下一個棧幀卻到了jvm的棧中,頗有趣的一個東西。

  8.咱們方法調用完畢,棧中棧幀彈出,棧清理完畢;而後gc會對java堆中對象進行回收釋放內存空間,而後gc還會對方法區進行清理,自此jvm中的內存空間清理完畢;

  9 操做系統堆jvm進行清理,jvm進程結束。

 

總結: 

  因爲對操做系統的理解還處於比較懵懂的狀態,因此文中可能會有不少詞語運用不當,很慚愧,之後確定會抽個時間慢操做系統整個的學習一遍的,但願這一天不要來的太遲,哈哈哈

相關文章
相關標籤/搜索