初步瞭解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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...