JVM中有哪些記憶體區域,分別是用來乾什麼的

来源:https://www.cnblogs.com/zhxiansheng/archive/2019/07/14/11185840.html
-Advertisement-
Play Games

前言 之前我們探討過一個.class文件是如何被載入到jvm中的。但是jvm內又是如何劃分記憶體的呢?這個內被載入到了那一塊記憶體中?jvm記憶體劃分也是面試當中必被問到的一個面試題。 什麼是jvm記憶體區域劃分? 其實這個問題非常簡單,JVM在運行我們寫好的代碼時,他是必須使用多塊記憶體空間的,不同的記憶體空 ...


前言

之前我們探討過一個.class文件是如何被載入到jvm中的。但是jvm內又是如何劃分記憶體的呢?這個內被載入到了那一塊記憶體中?jvm記憶體劃分也是面試當中必被問到的一個面試題。

什麼是jvm記憶體區域劃分?

其實這個問題非常簡單,JVM在運行我們寫好的代碼時,他是必須使用多塊記憶體空間的,不同的記憶體空間用來放不同的數據,然後配合我們寫的代碼流程,才能讓我們的系統運行起來。

舉個最簡單的例子,比如咱們現在知道了JVM會載入類到記憶體里來供後續運行,那麼我問問大家,這些類載入到記憶體以後,放到哪兒去了呢?想過這個問題嗎?

所以JVM里就必須有一塊記憶體區域,用來存放我們寫的那些類。

包括我們定義的成員變數,類變數,方法,局部變數等等,都在jvm記憶體中對應著一塊記憶體來記錄存儲。

存放類的方法區

在JDK1.8之前的版本里,代表JVM的一塊區域。在1.8版本以後,這塊區域的名字改了,叫做“Matespace”,可以認為是“元數據空間”這樣的意思,當然這裡主要存放的還是我們自己寫的各種類的相關信息。

舉個慄子。有如下兩個類,People類沒有成員變數,而Student類有一個name的類變數。

public class Student{
    private static String name = "lisi";
}

public class People{
    public static void main(){
        Student student = new Student();
    }
}

這兩個類被載入到JVM,就會存放在這個方法區裡面(註意:如果讀過我之前的章節,就會明白這裡的載入代表的是:載入->驗證->準備->解析->初始化,類的所有類變數都會被賦值)。如下圖

image

執行代碼指令的程式計數器

我們知道,被載入到jvm的類對象是我們寫的.java文件被編譯之後的.class文件。

在編譯過後會將我們的代碼編譯成電腦能讀懂的位元組碼。而這個.calss文件就是,就是我們代碼編譯好的位元組碼了。

載入到記憶體以後,位元組碼執行引擎就開始工作了。去執行我們編譯出來的代碼指令,如下圖

image

此時問題來了,我們是不是需要一塊記憶體空間來記錄我們位元組碼執行引擎目前執行到了哪行代碼?這一塊特殊的記憶體區域就是“程式計數器”

這個程式計數器就是用來記錄當前執行的位元組碼指令的位置。

如下圖:

image

到這裡我相信會有人產生疑惑,就按照當前的代碼順序執行就行了,為什麼要記錄執行到哪裡了?

因為我們寫好的代碼可能會開啟多個線程併發的執行不同的代碼。可能當前線程這段代碼還沒有執行完畢,就上下文切換到另一段代碼中。

當線程再次上下文切換到之前的代碼時,就需要一個專門記錄當前線程執行到了哪一條位元組碼。所以,每一個線程都有這自己的程式計數器。

如下圖:

image

java虛擬機棧

java代碼在執行的時候,一定是某個線程來執行某個方法中的代碼。

當線程執行到某個方法的時候,如果這個方法有局部變數,那麼就需要一塊區域來存放局部變數的數據信息。這個區域就叫做java虛擬機棧

每一個線程都有一個自己的java虛擬機棧,比如說當執行main方法的時候就會有一個main線程,用來存放main方法中定義的局部變數

public static void main(){
    People people = new People();
    int i = 9;
}

比如上面的main()方法中,其實就有一個"people"的局部變數,他是引用一個People的實例對象的,這個對象我們先不管他。然後有一個"i"的局部變數。

如下圖:

image

我想大家應該都知道棧的數據結構,後進先出。當方法執行完畢以後,這個棧楨就會出棧,裡面的局部變數信息就會從記憶體刪除。所以局部變數是線程安全的。因為只有當前線程能獲取到這個值。

為什麼要用後進先出的數據結構?

假設a方法當中同步調用b方法,此時a方法的棧楨先入棧,然後再是b方法的棧楨入棧。b方法執行完畢後,b方法的棧楨出棧,繼續執行a方法。所以使用一個後進先出的棧結構是非常完美的。

此時jvm的記憶體模型圖如下:

image

java堆記憶體

這一塊記憶體是非常非常重要的。

我們實例化的所有對象都是存放在這個記憶體中。這個實例化的對象裡面會包含一些數據,我們用上面的代碼來做慄子。

public class Student{
    private String name = "lisi";
    public String getNmae(){
        return name;
    }
}

public class People{
    public static void main(){
        Student student = new Student();
        student.getName();
    }
}

還是這個代碼,當main線程執行main()方法的時候,首先在堆記憶體中實例化Student對象,然後在局部變數中創建student,student存的是實例化Student對象的記憶體地址。然後執行Student對象的getName()方法。

如下圖:

image

由上圖可以看出來,棧空間是封閉的,是線程安全的,而堆記憶體中是我們主要發生線程不安全的地方,因為堆記憶體的空間所有的線程其實都是能共用的。

此時jvm的記憶體劃分的最終模型為:

image

其他記憶體區域

很多java程式猿對這一塊區域的接觸是非常少的。

其實在JDK的很多底層代碼API中,比如NIO。

如果你去看源碼會發現很多地方的代碼不是java寫的,而是走的native方法去調用本地操作系統裡面的一些方法,可能調用的都是c語言寫的方法。

比如說:public native int hashCode();

在調用這種native方法的時候,就會有線程對應的本地方法棧,這個其實類似於java虛擬機棧。也是存放各種native方法的局部變數表之類的信息。

還有一塊區域,是不是jvm的,通過NIO中的allocateDirect這種API,可以在jva堆外分配記憶體空間,然後通過java虛擬機棧里的DirectByteBuffer來引用和操作堆外記憶體空間。

總結

基本上jvm的核心記憶體區域的功能都解釋清楚了,面試能回答到這一個地步應該也能順利通過了。

我們需要重點關註的是方法區,程式計數器,java虛擬機棧和java堆記憶體這些記憶體區域的作用。


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

-Advertisement-
Play Games
更多相關文章
  • 新聞 "FableConf門票開始販售" "Bolero的HTML模板支持熱載入" "Bolero從v0.4到v0.5的升級指南" "完整的SAFE Chat遷移至了Fable 2" "為純函數式3D圖形生成領域專用語言" "使用Roslyn分析器更快捷地寫出更好的代碼" 視頻及幻燈片 "介紹用F ...
  • 聲明引用的同時必須對其初始化,否則系統會報錯,所以我們是無法這樣使用 vector<int &> vec; 這條語句會報錯,同時引用不是定義一個新的變數或對象,因此記憶體不會為引用開闢新的空間存儲這個引用 而vector將無法開闢存儲空間。 參考博客: https://www.cnblogs.com/ ...
  • 使用Mybatis完成的CRUD操作 個人總結的一些小規律 學習過程中碰到的錯誤: 雖然報錯信息提示的 很明顯是sql語法報錯,不過我一時之間沒有沒有看出sql語句錯在哪裡,後來才發現是插入sql語句的括弧寫錯了,不是小括弧,而是大括弧 JavaBean類 dao層 配置文件 測試類 typeAli ...
  • 緩存鎖  我們常常將緩存作為分散式鎖的解決方案,但是卻不能單純的判斷某個 key 是否存在 來作為鎖的獲得依據,因為無論是 exists 和 get 命名都不是線程安全的,都無法保證只有一個線程可以獲得鎖,存線上程爭搶,可能會有多個線程同時拿到鎖的情況(經典的 Redis “讀後寫”的問題 ...
  • 一、mybatis逆向工程 由官方自動生成dao mapper.xml pojo等文件步驟:1)、導入jar包: mybatis-generator-core-1.3.6 代碼生成器的核心包 mysql-connector-java-5.1.28-bin.jar 連接資料庫 mybatis-3.2. ...
  • SQL映射器Mapper介面 MyBatis基於代理機制,可以讓我們無需再寫Dao的實現。直接把以前的dao介面定義成符合規則的Mapper。 註意事項: 1.介面必須以Mapper結尾,名字是DomainMapper 2.mapper.xml文件要和Mapper介面建立關係,通過namespace ...
  • 1.網上大部分都是這種方法 註釋掉 tomcat 9 安裝目錄下的conf里的 logging.properties 找到 java.util.logging.ConsoleHandler.encoding = UTF-8 將其註釋掉,或改為 GBK 2.第二種方法,修改JAVA預設語言 在大部分w ...
  • python預設參數陷阱 0|1陷阱? 學過函數的人一定聽說過函數的預設參數,關於函數的預設參數,請看以下的例子: def extendList(val, lst=[]): lst.append(val) return lst list1 = extendList(10) list2 = exten ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...