初步瞭解JVM第二篇

来源:https://www.cnblogs.com/linzepeng/archive/2019/12/18/12063986.html
-Advertisement-
Play Games

在一篇《初步瞭解JVM第一篇》中,我們已經瞭解了: 類載入器:負責載入*.class文件,將位元組碼內容載入到記憶體中。其中類載入器的類型有如下: 啟動類載入器(Bootstrap) 擴展類載入器(Extension) 應用程式類載入器(AppClassLoader) 用戶自定義載入器(User-Def ...


在一篇《初步瞭解JVM第一篇》中,我們已經瞭解了:

  • 類載入器:負責載入*.class文件,將位元組碼內容載入到記憶體中。其中類載入器的類型有如下:
    • 啟動類載入器(Bootstrap)
    • 擴展類載入器(Extension)
    • 應用程式類載入器(AppClassLoader)
    • 用戶自定義載入器(User-Defined) 
  • 執行引擎:負責解釋命令,提交給操作系統執行。
  • 本地介面:目的是為了融合不同的編程語言提供給Java所用,但是企業中已經很少會用到了。
  • 本地方法棧:將本地介面的方法在本地方法棧中登記,在執行引擎執行的時候載入本地方法庫
  • PC寄存器:是線程私有的,記錄方法的執行順序,用以完成分支、迴圈、跳轉、異常處理、線程恢復等基礎功能。

那在這一篇中我們來聊一聊方法區、棧和堆。

繼上一篇順序的PC寄存器

5.方法區

在JVM的架構圖中,Java棧、本地方法棧、程式計數器都是線程私有的。而方法區跟堆一樣,是一個記憶體共用的區域,他的主要作用就是存儲每一個類的結構信息,例如運行時常量池(Runtime Constant Pool)、欄位和方法數據、構造函數和普通方法的位元組碼內容。

再簡單來說方法區就是一個類的模板,在上一篇我們已經說了ClassLoader將class文件載入完成之後會把類的位元組碼內容放到方法區中,就像把Car.class文件通過類載入器載入後,會把car這個類的結構信息存放在方法區中。當你要實例化的時候再通過這個模板去new出你想要的car1,car2,car2,而你創建出來這些類對象是存放在堆(heap)中的。

圖一是方法區中存放的內容

 圖一

方法區的實現:

方法區只是一個定義、一個規範。在不同的虛擬機裡頭實現是不一樣的。這裡我們主要介紹的是JDK7和JDK8的實現方式

  • JDK7:永久代(PermGen space)
  • JDK8:元空間(Metaspace)

永久代

在JDK7中方法區的實現方式叫永久代,但是它存儲的部分數據是存放在JVM的一塊地方的,這會造成一個問題:

當類載入太多了,可能會導致記憶體棧溢出:java.lang.OutOfMemoryError: PermGen這樣一來就不夠靈活,為了提高靈活性(這隻是其中一個原因)就有了元空間

元空間:

在JDK8中,JVM的開發者就把永久代移除了,移至元空間中。其實作用是差不多的,只是元空間不再使用JVM的記憶體了,而是直接使用本地堆記憶體(native heap),說白了就是直接使用系統的記憶體,這樣就幾乎不會發生記憶體溢出的情況,提高了靈活性。

所以為什麼在網上會看到關於方法區很多不同的說法就是因為方法區的實現方式在不同的JVM中是不同,最典型的就是永久代和元空間。

以上我們總結出:

  • 方法區:類似一個模板,存儲一個類的結構信息。
  • 實現方法:
    • 永久代:使用JVM的記憶體。
    • 元空間:使用系統記憶體。

以上就是方法區的介紹,在介紹堆的時候還會提及。

6.Stack棧

棧是一個線程私有的,主要用來管理Java程式的運行。是線上程創建的時候創建的,它的生命周期跟隨這線程的結束而結束,當線程結束了棧的記憶體也就釋放了,對於棧來說,不會存在垃圾回收問題,因為只要線程一結束該棧就結束了。

棧中主要存儲的內容:

  • 8種基本數據類型
  • 對象的引用變數
  • 實例方法

棧就類似一個子彈夾,它的特點就是“後進先出,先進後出”,在Java中需要實現很多方法,而這些方法就是一個一個被壓進棧中的,然後再依次調用。在平常中,我們所說的Java中的方法在棧其實有一個專有名詞叫棧幀,棧幀主要存放三類數據:

  • 本地變數(Local Variables):輸入參數和輸出參數以及方法內的變數。
  • 棧操作(Operand Stack):記錄出棧、入棧的操作。
  • 棧幀數據(Frame Data):包括類文件、方法等等。

棧運行原理:

Java中的方法存放在棧中,但是這些方法到底是怎麼執行的呢?

接下來我們就用一個例子來說明一下:

package testJVM;

public class TestStack {
    public  static void method_one(){
        System.out.println("This is the method_one");
    }
    public static void method_two(){
        System.out.println("This is the method_two");
    }
    public static void main(String[] args) {
        System.out.println("This is the main method");
        //調用方法一
        method_one();
        //調用方法二
        method_two();
        //輸出程式結束
        System.out.println("The program is finish");
    }
}

以上的運行結果為:

這樣的輸出結果,相信已經在大家的預料之中,但是這些方法在棧中是怎麼運行的呢?廢話不說,上圖二

圖二

我們都知道main方法是一切程式的入口,所以程式一執行碰到的是main方法,main方法就第一個入棧了,所以他們的執行過程是這樣的:

  • 程式執行碰到第一個方法是main方法,main方法入棧。
  • main方法遇到的方法是method_one,將其入棧。
  • 再遇到的下一個方法是method_two,將其放入棧。

所以就形成了圖二,當他運行的時候:

  • 彈出method_two方法,在我們圖三中的箭頭就是PC寄存器的作用,所以在執行method_two,我們需要調用method_one方法。
  • 彈出method_one,下一步,我們看到圖二有指針指向main方法。
  • 彈出main方法,全部出棧。

 這樣就形成了類似一條執行鏈,依次執行了main方法。

總結棧運行原理:

棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個記憶體區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集,當一個方法A被調用時就產生了一個棧幀 F1,並被壓入到棧中, A方法又調用了 B方法,於是產生棧幀 F2 也被壓入棧, B方法又調用了 C方法,於是產生棧幀 F3 也被壓入棧, 執行完畢後,先彈出F3棧幀,再彈出F2棧幀,再彈出F1棧幀…… 遵循“先進後出”和“後進先出”原則。每個方法執行的同時都會創建一個棧幀,用於存儲局部變數表、操作數棧、動態鏈接、方法出口等信息,每一個方法從調用直至執行完畢的過程,就對應著一個棧幀在虛擬機中入棧到出棧的過程。棧的大小和具體JVM的實現有關,通常在256K~756K之間,與等於1Mb左右。

棧溢出

講完了棧的內容,現在我們來看一個大家在實際開發中會碰到的一個錯誤,請看下列代碼:

package testJVM;

public class TestStack {
    public  static void method_one(){
        //遞歸調用
        method_one();
    }
    public static void main(String[] args) {
        method_one();
    }
}

上述是一個遞歸調用的例子,現在來執行一下,看看會出現一個什麼結果:

 相信大家多多少少都會遇到過上述的錯誤,棧溢出。原因如下:

由於我們的方法method_one一直在遞歸調用自己,而且並沒有停止的條件。所以method_one這個方法就會被一直壓入棧中,JVM中的記憶體又是有限的,上述我們也提到了Java中的棧是隨著線程的生命周期結束而結束的,不會存在垃圾回收機制,記憶體得不到釋放而方法又不斷的進棧,最終記憶體不夠造成棧溢出的現象。圖三

圖三

以上就是本人對棧的理解,最後來到了重頭戲堆(heap),那就下篇再進行介紹吧,哈哈哈。

在下篇將會介紹:

  • 堆(heap)
  • GC垃圾回收機制

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 分庫分表之第一篇 1.概述 1.1.分庫分表是什麼 1.2.分庫分表的方式 1.2.1.垂直分表 1.2.2.垂直分庫 1.2.3.水平分庫 1.2.4.水平分表 1.2.5 小結 1.3.分庫分錶帶來的問題 1.3.1.事務一致性問題 1.3.2.跨節點關聯查詢 1.3.3.跨節點分頁、排序函數 ...
  • 1、面向對象的四個優點:可復用、可拓展、可維護、靈活性高。編寫代碼的時候一定要牢記:靈活運用面向對象的三大特征:封裝、繼承、多態,降低代碼之間的耦合,避免做無用功,避免代碼不可維護。ps:不要懶,現在懶只會讓將來不得不勤快 ...
  • 0、前言 任何系統,我們不會傻傻的在每一個地方進行異常捕獲和處理,整個系統一般我們會在一個的地方統一進行異常處理,spring boot全局異常處理很簡單; 介紹前先說點題外話,我們現在開發系統,都是前後端完全分離的,後端只提供RESTfull API,禁止涉及任何界面,什麼thymeleaf、JS ...
  • 背景 一次無意的訪問,點擊到了一個專門做PHP性能測試的網站,看這裡 "PHP Benchmarks" 。 在裡面發現了框架性能測試的結果,發現Laravel的框架性能盡然是最低的。瞬間受到了一萬點的暴擊,誰讓最近一直用Laravel開發項目的呢。 說到底還是Laravel好用呀,方便不說,各方面支 ...
  • 在java類中使用super來調用父類中的指定操作: super可用於訪問父類中定義的屬性; super可用於調用父類中定義的成員方法; super可用於在子類構造方法中調用父類的構造方法; 註意: 尤其當父子類出現同名成員時,可以用super進行區分; super的追溯不僅限於直接父類,還可以調用 ...
  • 使用Spring Boot 本節將詳細介紹如何使用Spring Boot。它涵蓋了諸如構建系統,自動配置以及如何運行應用程式之類的主題。我們還將介紹一些Spring Boot最佳實踐。儘管Spring Boot並沒有什麼特別的地方(它只是另一個可以使用的庫),但是有一些建議可以使您的開發過程更輕鬆一 ...
  • 該參數主要是控制新生代需要經歷多少次GC晉升到老年代中的最大閾值。在JVM中用4個bit存儲(放在對象頭中),所以其最大值是15。如果該值=0則表示對象在Eden區gc後不經過Survivor區直接進入老年代。如果該值等於15,並非意味著,對象必須要經歷15次YGC才會晉升到老年代中。例如,當sur... ...
  • 一 Netty服務端NioEventLoop的啟動 Netty服務端創建、初始化完成後,再向Selector上註冊時,會將服務端Channel與NioEventLoop綁定,綁定之後,一方面會將服務端Channel的註冊工作當做Runnable任務提交到NioEventLoop的taskQueue, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...