java中棧記憶體與堆記憶體(JVM記憶體模型)

来源:https://www.cnblogs.com/ITnoteforlsy/archive/2020/02/05/12266409.html
-Advertisement-
Play Games

java中棧記憶體與堆記憶體(JVM記憶體模型) Java中堆記憶體和棧記憶體詳解1 和 Java中堆記憶體和棧記憶體詳解2 都粗略講解了棧記憶體和堆記憶體的區別,以及代碼中哪些變數存儲在堆中、哪些存儲在棧中。記憶體中的堆和棧到底是什麼 詳細講述了程式在記憶體中的模型,從可執行文件(ELF)格式的編譯介紹了堆和棧,主要是... ...


java中棧記憶體與堆記憶體(JVM記憶體模型)

 

Java中堆記憶體和棧記憶體詳解1 和 Java中堆記憶體和棧記憶體詳解2 都粗略講解了棧記憶體和堆記憶體的區別,以及代碼中哪些變數存儲在堆中、哪些存儲在棧中。記憶體中的堆和棧到底是什麼 詳細講述了程式在記憶體中的模型,從可執行文件(ELF)格式的編譯介紹了堆和棧,主要是C/C++語言,講的比較清楚,借鑒性比較強。

其實,對於java語言,編譯後的文件是一個中間位元組代碼,操作系統不能直接執行,需要jvm解釋執行。與C/C++對應起來,理解java的棧記憶體和堆記憶體,應該從jvm的記憶體模型入手(參考深入理解JVM-記憶體模型(jmm)和GC)。

一、Java記憶體模型

java程式記憶體的分配是在JVM虛擬機記憶體分配機制下完成。

java記憶體模型(Java Memory Model ,JMM)就是一種符合記憶體模型規範的,屏蔽了各種硬體和操作系統的訪問差異的,保證了Java程式在各種平臺下對記憶體的訪問都能保證效果一致的機制及規範。

根據java虛擬機規範,java虛擬機管理的記憶體將分為下麵五大區域。

1.程式計數器

程式計數器(Program Counter Register)是一塊較小的記憶體空間,它可以看做是當前線程所執行的位元組碼的行號指示器。在虛擬機的概念模型里(僅是概念模型,各種虛擬機可能會通過一些更高效的方式去實現),位元組碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。

Java虛擬機的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現,也就是說,在同一時刻一個處理器內核只會執行一條線程,處理器切換線程時並不會記錄上一個線程執行到哪個位置,所以為了線程切換後依然能恢復到原位,每條線程都需要有各自獨立的程式計數器。

特點

  • 線程私有
  • JVM規範中唯一沒有規定OutOfMemoryError情況的區域
  • 如果正在執行的是Native 方法,則這個計數器值為空

2. java棧(虛擬機棧)(具體參考JVM 系列 - 記憶體區域 - Java 虛擬機棧(三)

Java 虛擬機棧中的單位元素是棧幀,每個線程中調用同一個方法或者不同的方法,都會創建不同的棧幀。在 Running 的線程,只有當前棧幀有效(Java 虛擬機棧中棧頂的棧幀),與當前棧幀相關聯的方法稱為當前方法。每調用一個新的方法,被調用方法對應的棧幀就會被放到棧頂(入棧),也就是成為新的當前棧幀。當一個方法執行完成退出的時候,此方法對應的棧幀也相應銷毀(出棧)。

每個棧幀中存放局部變數表、操作數棧、動態鏈接、方法返回地址、附加信息。

3. 本地方法棧

本地方法棧(Native Method Stacks)與 Java 虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧為虛擬機執行 Java 方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機使用到的 Native 方法服務。虛擬機規範中對本地方法棧中的方法使用的語言、使用方式與數據結構並沒有強制規定,因此具體的虛擬機可以自由實現它。

Navtive 方法是 Java 通過 JNI 直接調用本地 C/C++ 庫,可以認為是 Native 方法相當於 C/C++ 暴露給 Java 的一個介面,Java 通過調用這個介面從而調用到 C/C++ 方法。當線程調用 Java 方法時,虛擬機會創建一個棧幀並壓入 Java 虛擬機棧。然而當它調用的是 native 方法時,虛擬機會保持 Java 虛擬機棧不變,也不會向 Java 虛擬機棧中壓入新的棧幀,虛擬機只是簡單地動態連接並直接調用指定的 native 方法。

4.堆(參考JVM 系列 - 記憶體區域 - Java 堆(五)

Java 堆(Java Heap)是 Java 虛擬機所管理的記憶體中最大的一塊,也被稱為 "GC堆",是被所有線程共用的一塊記憶體區域,在虛擬機啟動時被創建。

唯一目的就是儲存對象實例和數組(JDK7 已把字元串常量池和類靜態變數移動到 Java 堆),幾乎所有的對象實例都會存儲在堆中分配。隨著 JIT 編譯器發展,逃逸分析、棧上分配、標量替換等優化技術導致並不是所有對象都會在堆上分配。

Java 堆是垃圾收集器管理的主要區域。堆記憶體分為新生代 (Young) 和老年代 (Old) ,新生代 (Young) 又被劃分為三個區域:Eden、From Survivor、To Survivor。

根據 Java 虛擬機規範的規定,Java 堆可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可,就像我們的磁碟空間一樣。在實現時,既可以實現成固定大小的,也可以是可擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(通過 -Xmx 和 -Xms 控制)。

5.方法區(https://www.jianshu.com/p/59f98076b382

方法區(Method Area)與 Java 堆一樣,是所有線程共用的記憶體區域。

JDK7 之前(永久代)用於存儲已被虛擬機載入的類信息、常量、字元串常量、類靜態變數、即時編譯器編譯後的代碼等數據。

Java 虛擬機規範把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做 Non-Heap(非堆),目的應該是與 Java 堆區分開來。

運行時常量池(Runtime Constant Pool)是方法區的一部分。Class 文件中除了有類的版本/欄位/方法/介面等描述信息外,還有一項信息是常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將類在載入後進入方法區的運行時常量池中存放。運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的是 String.intern() 方法。

java 中基本類型的包裝類的大部分都實現了常量池技術,這些類是 Byte、Short、Integer、Long、Character、Boolean,另外 Float 和 Double 類型的包裝類則沒有實現。另外 Byte、Short、Integer、Long、Character 這5種整型的包裝類也只是在對應值在-128到127之間時才可使用對象池。

在老版jdk,方法區也被稱為永久代(可以通過 -XX:PermSize 和 -XX:MaxPermSize 來進行調節大小),JDK8 徹底將永久代移除出 HotSpot JVM,將其原有的數據遷移至 Java Heap 或 Native Heap(Metaspace),取代它的是另一個記憶體區域被稱為元空間(Metaspace)。

元空間(Metaspace):元空間是方法區的在 HotSpot JVM 中的實現,方法區主要用於存儲類信息、常量池、方法數據、方法代碼、符號引用等。元空間的本質和永久代類似,都是對 JVM 規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地記憶體。元空間的大小理論上取決於32位/64位系統記憶體大小,可以通過 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 配置記憶體大小。

 

二、常說的java中的棧記憶體和堆記憶體

我們經常說的棧記憶體和堆記憶體只是java記憶體模型中的一部分內容,也就是編程過程中關註比較多的部分。

通常說的棧一般指棧幀中的局部變數表(存放的8種類型: byte、short、int、long、float、double、char、boolean和reference、returnAddress),它是一片連續的記憶體空間,用來存放方法參數,以及方法內定義的局部變數,存放著編譯期間已知的數據類型。局部變數表所需要的記憶體空間在編譯期完成分配,當進入一個方法時,這個方法在棧中需要分配多大的局部變數空間是完全確定的,在方法運行期間不會改變局部變數表大小。

通常說的堆一般指java記憶體模型中的堆,用於儲存對象實例和數組,幾乎所有的對象實例都會存儲在堆中分配。java堆是java虛擬機管理的記憶體中最大的一塊,也被稱為 "GC堆",是垃圾收集器管理的主要區域。

三、棧記憶體和堆記憶體的區別(只是便於記憶,並不嚴謹

  1. 存儲的數據及生命周期

    棧主要用於存儲方法參數、局部變數和對象的引用變數,存放的是編譯期間已知的數據類型(八大基本類型和對象引用(reference類型),returnAddress類型。每個線程都會有一個獨立的棧空間,棧記憶體的數據生命周期隨線程的結束而結束。

    所有對象實例及數組都要在堆上分配記憶體,堆存放的對象是線程共用的,線程結束時,對象實例和數組的生命周期並不一定結束,只有被GC回收後生命周期結束。

  2. 空間大小及限制

    棧的記憶體大小在編譯時確定,是一段連續的空間,運行時不會改變,棧記憶體隨線程的結束自動回收。如果請求的棧的深度大於虛擬機允許的棧深度,JVM會拋出java.lang.StackOverFlowError。

    堆記憶體在程式運行時動態分配,可以是存在物理上不連續的記憶體空間,線程運行結束後GC進行回收(只有對象或數組不再被引用時才回收)。如果是堆記憶體沒有可用的空間存儲生成的對象,JVM會拋出java.lang.OutOfMemoryError。

  3. 獨占或共用

    棧記憶體歸屬於單個線程,每個線程都會有一個棧記憶體,其存儲的變數只能在其所屬線程中可見,即棧記憶體可以理解成線程的私有記憶體。而堆記憶體中的對象對所有線程可見。堆記憶體中的對象可以被所有線程訪問。

  4. 分配效率

    棧由系統自動分配,速度較快。堆由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便。

  5. 存取速度(jvm中可能會不同)

    由於很多CPU對壓棧、出棧操作有硬體(指令)上的支持,所以在棧區分配/歸還記憶體速度極快(相比之下,堆上分配簡直是龜速);尤其是函數內部的局部變數,可以輕易與函數調用/返回綁定,因此幾乎所有編譯型語言都會在利用棧管理局部變數(而且會優先使用空閑的寄存器,所以幾乎所有高級語言都是訪問局部變數速度最快)。


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

-Advertisement-
Play Games
更多相關文章
  • 1. 切勿混用帶符號類型和無符號類型 如果表達式里既有帶符號類型又有無符號類型,帶符號類型會自動轉換為無符號類型。 2. 列表初始化 列表初始化當用於內置類型時,如果存在精度丟失編譯器將報錯。 3. 變數聲明和定義的關係 4. C++操作符代替名 5. const限定符 6. 類型別名 7. dec ...
  • 親愛的兒子: 當你打開這封信的時候,我已經離開波士頓回到加州了,你也已經結束自己最後一個暑假,去往自己非常喜歡的公司。 雖然你我都是程式員,但是你卻很少向我咨詢過技術相關的問題,咱們上一次一起寫代碼,也是你高考結束那個暑假了。不過前幾天你問了我一個問題,你說,如果讓你寫一封信,跟當初剛剛成為程式員的 ...
  • That syntax is called an indexed part-select. The first term is the bit offset and the second term is the width. It allows you to specify a variable f ...
  • 官方文檔 猛戳這裡 在settings中配置以下代碼 #LOGGING_DIR 日誌文件存放目錄 LOGGING_DIR = "logs" # 日誌存放路徑 if not os.path.exists(LOGGING_DIR): os.mkdir(LOGGING_DIR) import loggin ...
  • 伴隨著移動互聯網的飛速發展,越來越多用戶被互聯網連接在一起,用戶所積累下來的數據越來越多,市場對數據方面人才的需求也越來越大,由此也帶火瞭如數據分析、數據挖掘、演算法等職業,而作為其中入門門檻相對較低、工資高於大多傳統行業崗位的數據分析一職,則成為了許多想轉行進入數據領域的同學的首要選擇。 那麼在現在 ...
  • A類調用B類的靜態方法,除了載入B類,但是B類的一個未被調用的方法間接使用到的C類卻也被載入了,這個有意思的場景來自一個提問: "方法中使用的類型為何在未調用時嘗試載入?" 。 場景如下: 添加JVM varbose參數進行執行,輸出是: main方法執行 ,而 方法裡面只有列印語句,所以理論上應該 ...
  • 要分析JVM的源碼,結合資料直接閱讀是一種方式,但是遇到一些想不通的場景,必須要結合調試,查看執行路徑以及參數具體的值,才能搞得明白。所以我們先來把JVM的源碼進行編譯,並能夠使用GDB進行調試。 編譯環境 本文使用的JDK版本:OpenJDK7,分支b147 下載頁面:https://downlo ...
  • numpy庫中矩陣的常用方法 1 矩陣轉置 從上圖可以看出:使用方法a.T可以將矩陣a轉置。 2 均值與方差 註意:方法a.mean()會對矩陣a的所有元素求均值,a.var()也是考慮矩陣a的所有元素求方差。 當然,也可以選取矩陣的某一行或某一列使用mean與var求均值與方差。 3 設置零矩陣 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...