JVM內存分析,以及一步步詳解Java程序是如何運行的?

雲棲號資訊:【點擊查看更多行業資訊
在這裏您能夠找到不一樣行業的第一手的上雲資訊,還在等什麼,快來!
java


VM也就是Java虛擬機,它的內存結構這塊知識點。linux

你說它重要吧,編寫代碼基本用不到它;程序員

你說它不重要吧,程序員想要進階又必須對底層有必定的瞭解。數組

最終仍是決定更加深刻地學習下JVM,同時也用本身的理解詳細地說明Java程序是如何運行的。安全

固然本人目前的能力有限,只能說盡己之能學的越多越好。oracle

1、Java程序開發三步驟工具

編寫、編譯和運行,圖解以下:post

1

①編寫階段學習

後綴名爲.Java的文件,也就是所謂的源碼。開發工具

可是Java虛擬機它並不認識.Java文件,Java虛擬機和Java語言其實並無必然的聯繫。

其實我挺想吐槽的,大家長的這麼像,居然不認識?

那爲什麼不認識呢?

按照我我的的理解,咱們常說的代碼、Java語言,其實本質上仍是人類定義的一門語言,主要由英文組成,Java虛擬機 自己並不認識它。

因此須要將其編譯成Java虛擬機 認識的語言,即.class文件。

②編譯階段

後綴名爲.class的文件,也就是所謂的字節碼文件。

字節碼文件,就能夠理解成咱們寫代碼一個類(接口、枚舉、註釋)裏面的全部數據。

.class文件是如何來的?

javac編譯器編譯而來的,它能將.java文件編譯成.class文件,這樣的話JVM也就能認識.class文件了。

咱們平時寫的代碼其實也是Java源碼,只不過開發工具很強大,將這三個階段糅合在一塊兒了,以IDEA工具爲例:

2

src,其實就是源碼的意思,咱們平時編寫的類、接口、枚舉、註解等文件,其實本質上就是.java文件。

利用開發工具中的Show in Explorer功能,能夠找到計算機裏對應的文件夾文件。

out,這個我還不太懂具體是什麼意思,可是.class文件就在這個文件夾裏面。

若是不用開發工具,咱們須要用javac編譯器將.java文件編譯成.class文件;

開發工具等因而自動幫咱們編譯了,很是的方便,可是原理咱們要明白。

既然講到了.class文件,再回顧下反射中剛學的Class對象:

Class自己也是Java裏的一個類,它其實就是指的字節碼文件。

注意:小寫字母開頭的class是Java裏的一個關鍵字,建立一個類時類名前面都會由它來修飾。

因此Class類也就表示成:class Class{};

前面是關鍵字,後面是類名。

最後用一個例子來講明:

咱們在開發工具裏面編寫一個Student類;

那麼在對應的文件夾下就會有一個Student.java文件,也就是源碼;同時開發工具會給我編譯一個對應的Student.class文件,也就是字節碼文件;

而這個字節碼文件會有惟一的一個Class對象,專門用來描述該字節碼文件,在反射中能夠拿來使用,除此以外還能夠做爲線程鎖。

③運行階段

Java虛擬機是認識.class文件的,也就是說可以用來運行程序了。

但它具體是如何運行的呢?

就須要知道JDK了,圖解以下:

2

①JRE:Java Runtime Environment

翻譯過來就是Java運行環境,包含JVM和運行時所須要的核心類庫。

也就是說有了JRE就能夠運行Java程序了,可是隻能用來運行,若是出了bug,是無法修改的,因此須要JDK。

②JDK:Java Development Kit

翻譯過來就是Java程序開發工具包,包含JRE 和開發人員使用的工具。

前面提到的javac編譯器就是Java工具,JRE中是沒有javac編譯器的,因此它無法修改程序,畢竟沒有編譯的話JVM是不認識的。

Java虛擬機是認識字節碼文件的,而且本質上它就是一個字節碼翻譯器。

它能夠將字節碼文件翻譯成各個系統(Windows系統、Mac系統、linux系統)對應的機器碼;

這樣的話就能確保字節碼文件能在各個系統上正確運行。

這也就是Java所謂的跨平臺特性的由來。

接下來咱們就詳細地瞭解下Java虛擬機。

2、JVM內存結構

咱們看oracle最新的官方文檔,最權威的即是這個官方文檔,畢竟技術是不斷更新的:

3

JVM內存主要分爲這五大塊:

  • 程序計數器(The pc Register)
  • Java虛擬機棧(Stacks)
  • 堆內存(Heap)
  • 方法區(Method Area)
  • 本地方法棧(Native Method Stacks)

其中官方文檔中還有一個運行時常量池,暫且不考慮。

①stack棧

咱們常說的棧內存,其實嚴格上來講應該叫Java虛擬機棧。

利用開發工具IDEA斷點調試,咱們能夠很清晰地看到方法進棧出棧的過程。

5

這是一段很是簡易的代碼:main方法裏面調用method1方法,method1方法裏面調用method2方法。

利用右上角兩個功能鍵,能夠一步一步瞭解方法進棧出棧的過程。

Frames,翻譯過來就是幀的意思,一個方法對應一個棧幀。

什麼叫幀?

在影像動畫裏 ,一個鏡頭就是一幀;那麼棧幀就能夠理解成方法進棧的那一刻,就會造成一個棧幀。

每一個方法進棧都會有一個對應的棧幀。

此外、棧是先進先出原則,mian方法在最底下,最早進來,最後出去。

這個過程能夠用斷點調試模擬出來,感興趣的小夥伴能夠去嘗試下。

其中方法棧裏的線程是不共享的,什麼意思呢?

最直接地理解就是:棧裏面,不一樣的線程是獨立存在的,因此線程安全。

與之相對的是堆、方法區線程是共享的,這又是什麼意思呢?

最直接地理解就是:堆、方法區裏面的數據,不一樣的線程均可以拿過去用,因此線程不安全。

②heep:堆

咱們看oracle最新的官方文檔對其的說明,最權威的即是這個官方文檔,畢竟技術是不斷更新的:

6

這是翻譯成中文後的頁面,原文都是英文,英語好的小夥伴能夠直接看,不過翻譯大致上也還算是準確的:

  • 堆的線程是共享的(數據共享)
  • 堆的區域是用來存放對象和數組的(new 對象 new 數組,看到new這個關鍵字就能夠想到堆)
  • 堆在Java虛擬機啓動時建立。
  • 堆的大小能夠是固定的,也能夠擴展:使用「-Xms」與「-Xmx」控制堆的最小與最大內存。

此外對象是會建立不少的,其中一小部分會一直被使用到,大部分咱們使用後就會捨棄,因此這也是垃圾收集器管理的主要區域。

根據這兩種不一樣的狀況,垃圾收集器爲了更好地一一應對,堆內存又被劃分紅新生代和老年代:

  • 老年代:對象會一直被使用到,不會輕易地被回收
  • 新生代:對象朝生夕滅,須要常常回收

其中還有更加詳細地細分,實在是有點學不動了,暫時不考慮。

③方法區

一樣的,看官方文檔:

7

這個翻譯有點問題,但大致上也能看:

  • 方法區的線程是共享的。
  • 方法區在Java虛擬機啓動時建立。
  • 方法區的做用是存儲每一個類的結構。

既然是存儲類的結構,那麼.class文件也就是存儲在這個內存區域。

說白了就是類裏面的數據,都是存在這個方法區裏面的。

類中有什麼?

成員變量,成員方法等等,其中方法和變量還分靜態和非靜態。

這些都是存儲在方法區裏面的,除了類裏的數據,常量池也在方法區裏面。

方法運行時,會從方法區進入到棧裏面;

建立該類的對象時,會將建立的對象存放在堆裏面。

其中靜態變量隨着類的加載而加載,它並不會進堆。

以上三塊即是和咱們編寫Java代碼息息相關的內存區域,除此以外還有兩塊區域:

④程序計數器

咱們平時編寫的代碼能夠被反編譯成JVM指令,cpu就是依靠這個程序計數器執行指令的:

8

程序計數器會保存當前執行指令的地址,好比說程序計數器如今保存的爲0,那麼CPU會執行0對應的指令;

一旦指令執行,程序計數器將更新到下一條指令,上圖中也就是3,CPU接着會執行3對應的指令。

就這樣依次執行下去,程序計數器的做用就在於保證程序能正常運行。

⑤本地方法棧

①中的棧準確地說應該叫虛擬機棧,它服務的方法是Java方法;

本地方法棧做用和其是差很少的,不一樣的地方在於它服務的方法不是Java方法而是本地方法;

什麼叫本地方法?咱們其實見過,以下圖:

9

Objcet類中就有本地方法,被native這個關鍵字修飾,native就是本地的意思,這很好記憶。

也就是說該方法不是由Java語言寫的,而是由本地語言寫的,好比說C語言。

固然虛擬機規範中並未給出強制規定由什麼本地語言寫,不一樣的虛擬機能夠自由實現。

以上即是對JVM內存結的說明,除了JVM內存結構外,還有兩大塊……

3、JVM體系結構

源碼經過編譯成字節碼文件,再被類加載器加載進內存,最後通過各類交互被執行,大體就是這麼一個運行流程:

10

①類加載子系統(Class loader SubSystem)

它的做用是將類加載進內存,在Java中對應一個類ClassLoader,該類是一個抽象類。

  • 引導類加載器(Bootstrap ClassLoader)

它是由C++編寫的,是用來引導Java程序的,它並非ClassLoader子類。

  • 擴展類加載器(Extension ClassLoader)

它是由Java程序編寫的,負責加載java平臺中擴展功能的一些jar包。

  • 應用類加載器:(Application ClassLoader)

它是由Java程序編寫的,加載編寫的代碼。

②運行時數據區(Runtime Data Areas)

也就是JVM內存結構,上述已經詳細講解了,這也是對於程序員來講須要去重點了解的。

③執行引擎(Execution Engine)

解釋器interpreter:它是用於讀取字節碼文件,對其解釋並逐一執行,它的解釋速度較快,可是執行較慢,而且它有一個致命的缺點,就是當同一個方法被屢次調用時,它每次都要解釋。

即時編譯器JIT Compiler:它是用來優化解釋器的,解決上述的缺點。

垃圾回收器GC:收集並回收由new關鍵字建立的對象。

④本地方法接口和本地方法庫

存放本地方法。

總結:

以上即是對Java程序運行的詳解,以及對JVM內存的分析。

奈何本人能力實在是有限,還有一些地方法沒有弄明白,講解的也不算很深刻,但就我當前具有的能力而言,已經算是最詳細的解讀了。

【雲棲號在線課堂】天天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/live

當即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-06-06
本文做者:劉小愛
本文來自:「掘金」,瞭解相關信息能夠關注「掘金」

相關文章
相關標籤/搜索