女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

探討JVM的內存布局

Android編程精選 ? 來源:博客園 ? 作者:追風少年瀟歌 ? 2022-09-09 15:57 ? 次閱讀

內存布局

JVM內存布局規定了Java在運行過程中內存申請、分配、管理的策略,保證了JVM的穩定高效運行。不同的JVM對于內存的劃分方式和管理機制存在部分差異。結合JVM虛擬機規范,一起來探討jVM的內存布局。如下圖所示:

9777d98a-2424-11ed-ba43-dac502259ad0.png

Heap 堆區

Heap堆區是Java發生OOM(Out Of Memory)故障的地方,堆中存儲著我們平時創建的實例對象,最終這些不再使用的對象會被垃圾收集器回收掉,而且堆是線程共享的。一般情況下,堆所占用的內存空間是JVM內存區域中最大的,我們在平時編碼中,創建對象如果不加以克制,內存空間也會被耗盡。

堆的內存空間是可以自定義大小的,同時也支持在運行時動態修改,通過 -Xms 、-Xmx 這兩參數去改變堆的初始值和最大值。-X指的是JVM運行參數,ms 是memory start的簡稱,代表的是最小堆容量,mx是memory max的簡稱,代表的是最大堆容量;如 -Xms256M代表堆的初始值是256M,-Xmx1024M代表堆的最大值是1024M。

由于堆的內存空間是可以動態調整的,所以在服務器運行的時候,請求流量的不確定性可能會導致我們堆的內存空間不斷調整,會增加服務器的壓力,所以我們一般都會將JVM的Xms和Xmx的值設置成一樣,同樣也為了避免在GC(垃圾回收)之后調整堆大小時帶來的額外壓力。

堆區分為兩大區:Young區和Old區,又稱新生代和老年代。對象剛創建的時候,會被創建在新生代,到一定階段之后會移送至老年代,如果創建了一個新生代無法容納的新對象,那么這個新對象也可以創建到老年代。如上圖所示。

新生代分為1個Eden區和2個S區,S代表Survivor。大部分的對象會在Eden區中生成,當Eden區沒有足夠的空間容納新對象時,會觸發Young Garbage Collection,即YGC。在Eden區進行垃圾清除時,它的策略是會把沒有引用的對象直接給回收掉,還有引用的對象會被移送到Survivor區。

Survivor區有S0和S1兩個內存空間,每次進行YGC的時候,會將存活的對象復制到未使用的那塊內存空間,然后將當前正在使用的空間完全清除掉,再交換兩個空間的使用狀況。如果YGC要移送的對象Survivor區無法容納,那么就會將該對象直接移交給老年代。

上面說了,到一定階段的對象會移送到老年區,這是什么意思呢?每一個對象都有一個計數器,當每次進行YGC的時候,都會 +1。通過-XX:MAXTenuringThrehold參數可以配置當計數器的值到達某個閾值時,對象就會從新生代移送至老年代。

該參數的默認值為15,也就是說對象在Survivor區中的S0和S1內存空間交換的次數累加到15次之后,就會移送至老年代。如果參數配置為1,那么創建的對象就會直接移送至老年代。具體的對象分配即回收流程可觀看下圖所示。

978e5d4a-2424-11ed-ba43-dac502259ad0.png

如果Survivor區無法放下,或者創建了一個超大新對象,Eden和Old區都無法存放,就會觸發Full Garbage Collection,即FGG,便再嘗試放在Old區,如果還是容納不了,就會拋出OOM異常。在不同的JVM實現及不同的回收機制中,堆內存的劃分方式是不一樣的。

Metaspace 元空間

在JDK8版本中,元空間的前身Pern區已經被淘汰。在JDK7及之前的版本中,Hotspot還有Pern區,翻譯為永久代,在啟動時就已經確定了大小,難以進行調優,并且只有FGC時會移動類元信息。不同于之前版本的Pern(永久代),JDK8的元空間已經在本地內存中進行分配,并且,Pern區中的所有內容中字符串常量移至堆內存,其他內容也包括了類元信息、字段、靜態屬性、方法、常量等等都移至元空間內。

JVM Stacks 虛擬機棧

棧(Stack)是一個先進后出的數據結構,先進后出怎么理解?類似于我們平時打羽毛球時,裝羽毛球的球筒,第一個先放進去的往往最后一個才能拿出來,最后放進去的一個最先拿出來。

相對于基于寄存器的運行環境來說,JVM是基于棧結構的運行環境。因為棧結構移植性更好,可控性更強。JVM的虛擬機棧是描述Java方法執行的內存區域,并且是線程私有的。棧中的元素用于支持虛擬機進行方法調用,每個方法從開始調用到執行完成的過程,就是棧幀從入幀到出幀的過程。

在活動線程中,只有位于棧頂的幀才是有效的,稱為當前棧幀。正在執行的方法稱為當前方法,棧幀是方法運行的基本結構。在執行引擎運行時,所有指令都只能針對當前棧幀進行操作。而StackOverflowError表示請求的棧溢出,導致內存耗盡,通常出現在遞歸方法中。如果把JVM當做一個棋盤,虛擬機棧就是棋盤上的將/帥,當前方法的棧幀就是棋子能走的區域,而操作棧就是每一個棋子。操作棧的壓棧和出棧如下圖所示:

979ecef0-2424-11ed-ba43-dac502259ad0.png

虛擬機棧通過壓棧和出棧的方式,對每個方法對應的活動棧幀進行運算處理,方法正常執行結束,肯定會跳轉到另外一個棧幀上。在執行的過程中,如果出現異常,會進行異常回溯,返回地址通過異常處理表確定。棧幀在整個JVM體系中的地位頗高,包括局部變量表、操作棧、動態連接、方法返回地址等。

下面對棧幀的各個活動棧幀進行簡要的分析

(1)局部變量表

局部變量表是存放方法參數和局部變量的區域。我們都知道,類屬性變量一共要經歷兩個階段,分為準備階段和初始化階段,而局部變量是沒有準備階段,只有初始化階段,而且必須是顯示的。如果是非靜態方法,則在index[0]位置上存儲的是方法所屬對象的實例引用,隨后存儲的是參數和局部變量。字節碼指令中的STORE指令就是將操作棧中計算完成的局部變量寫回局部變量表的存儲空間內。

(2)操作棧

操作棧是一個初始狀態為空的桶式結構棧。在方法執行過程中,會有各種指令往棧中寫入和提取信息。JVM的執行引擎是基于棧的執行引擎,其中的棧指的就是操作棧。字節碼指令集的定義都是基于棧類型的,棧的深度在方法元信息的stack屬性中,下面就通過一個例子來說明下操作棧與局部變量表的交互:

publicintadd(){
intx=10;
inty=20;
intz=x+y;

returnz;
}

字節碼操作順序如下:

publicintadd();
Code:
0:bipush10//常量10壓入操作棧
2:istore_1//并保存到局部變量表的slot_1中(第1處)
3:bipush20//常量20壓入操作棧
5:istore_2//并保存到局部變量表的slot_2中
6:iload_1//把局部變量表的slot_1元素(intx)壓入操作棧
7:iload_2//把局部變量表的slot_2元素(inty)壓入操作棧
8:iadd//把上方的兩個數都取出來,在CPU里加一下,并壓回操作棧的棧頂
9:istore_3//把棧頂的結果存儲到局部變量表的slot_3中
10:iload_3
11:ireturn//返回棧頂元素值

第 1 處說明:局部變量表就像一個快遞柜,有著很多的柜子,依次編號為1,2,3,...,n,字節碼指令 istore_1 就代表打開了 1 號柜子,再把棧頂中的值 10 存進去。棧就好如一個桶,任何時候只能對桶口的元素進行操作,所以數據只能在棧頂進行存取。部分指令可以直接在柜子里面直接進行,比如 iinc指令,直接對抽屜里的數值進行 +1操作。我們經常遇到的 i++ 和 ++i,通過字節碼對比起來,答案一下子就一目了然了。如下表格所示:

97af836c-2424-11ed-ba43-dac502259ad0.jpg


左列中,iload_1 從局部變量表的第1號柜子取出一個數,壓入棧頂,下一步直接在柜子里實現 + 1的操作,而這個操作時對棧頂元素的值沒有任何影響,所以 istore_2 只是把棧頂元素賦值給 a,而右列,它是先在柜子里面進行 +1的操作,然后再通過 iload_1 把第1號柜子里的數壓入棧頂,所以istore_2賦給a的值是 +1 之后的值。擴展下,i++ 并非是原子操作。即使通過volatile關鍵字來修飾,多線程情況下,還是會出現數據互相覆蓋的情況。

(3)動態連接

每個棧幀中包含一個在常量池中對當前方法的引用,目的是支持方法調用過程的動態連接。

(4)方法返回地址

方法執行時有兩種退出情況:第一,正常退出,即正常執行到任何方法的返回字節碼指令,如 RETURN、IRETURN、ARETURN等;第二,異常退出。無論何種退出情況,都將返回方法當前被調用的位置。方法退出的過程相當于彈出當前棧幀,而退出可能有三種方式:

返回值壓入上層調用棧幀。

異常信息拋給能夠處理的棧幀。

PC 計數器指向方法調用后的下一條指令。

Native Method Stacks(本地方法棧)

本地方法棧(Native Method Stack)在JVM內存布局中,也是線程對象私有的,但是虛擬機棧“主內”,而本地方法棧“主外”。這個“內外”是針對JVM來說的,本地方法棧為Native方法服務。線程開始調用本地方法時,會進入一個不再受JVM約束的世界。本地方法可以通過JVNI(Java Native Interface)來訪問虛擬機運行時的數據區,甚至可以調用寄存器,具有和JVM相同的能力和權限。當大量本地方法出現時,勢必會削弱JVM對系統的控制力,因為它的出錯信息都比較黑盒,難以捉摸。對于內存不足的情況,本地方法棧還是會拋出 native heap OutOfMemory。

重點說下JNI類本地方法,最常用的本地方法應該是System.currentTimeMills(),JNI使Java深度使用操作系統的特性功能,復用非Java代碼。但是在項目過程中,如果大量使用其他語言來實現JNI,就會喪失跨平臺特性,威脅到程序運行的穩定性。假如需要與本地代碼交互,就可以用中間標準框架來進行解耦,這樣即使本地方法崩潰也不至于影響到JVM的穩定。

Program Counter Register (程序計數寄存器)

在程序計數寄存器(Program Counter Register,PC)中,Register的命名源于CPU的寄存器,CPU只有把數據裝載到寄存器才能夠運行。寄存器存儲指令相關的現場信息,由于CPU時間片輪限制,眾多線程在并發執行過程中,任何一個確定的時刻,一個處理器或者多核處理器中的一個內核,只會執行某個線程中的一個指令。

這樣必然會導致經常中斷或恢復,如何才能保證分毫無差呢?每個線程在創建之后,都會產生自己的程序計數器和棧幀,程序計數器用來存放執行指令的偏移量和行號指示器等,線程執行或恢復都要依賴程序計數器。程序計數器在各個線程之間互不影響,此區域也不會發生內存溢出異常。

小結

最后,從線程的角度來看,堆和元空間是所有線程共享的,而虛擬機棧、本地方法棧、程序計數器是線程內部私有的,我們以線程的角度再來看看Java的內存結構圖:

97bb8fae-2424-11ed-ba43-dac502259ad0.png





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 計數器
    +關注

    關注

    32

    文章

    2284

    瀏覽量

    96025
  • JVM
    JVM
    +關注

    關注

    0

    文章

    160

    瀏覽量

    12516
  • 虛擬機
    +關注

    關注

    1

    文章

    962

    瀏覽量

    29029

原文標題:圖文并茂:JVM 內存布局詳解

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    基于 IAR Embedded Workbench 的自研 MCU 芯片軟件函數與變量內存布局優化精控方法

    在嵌入式軟件開發領域,MCU芯片軟件的架構設計與內存布局的精細規劃對系統性能和穩定性起著關鍵作用。本文檔聚焦于IAR Embedded Workbench環境下,為自研MCU芯片軟件提供了一套詳盡
    的頭像 發表于 04-30 16:38 ?181次閱讀
    基于 IAR Embedded Workbench 的自研 MCU 芯片軟件函數與變量<b class='flag-5'>內存</b><b class='flag-5'>布局</b>優化精控方法

    模擬地和數字地混合PCB布局探討

    這是我的產品目前的電路拓撲,有一些疑問想跟大佬們共同探討一下。 圖1,模擬地和數字地分開,在電源入口處相連,紅色是模擬地的連接示意,綠色為數字電流的返回路徑。此方案感覺不妥當的是數字電流的返回
    發表于 04-29 22:35

    C語言中結構體與聯合體的深度解析:內存布局與應用場景

    例: struct Student { char name[20];// 20字節 int age;// 4字節 float score; // 4字節 }; 內存布局:總大小為32字節(考慮
    發表于 04-08 09:18

    虛擬內存不足如何解決 虛擬內存和物理內存的區別

    虛擬內存不足的解決方案 虛擬內存不足是計算機用戶經常遇到的問題,尤其是在運行大型軟件或多任務處理時。以下是一些解決虛擬內存不足問題的方法: 增加物理內存(RAM) : 這是最直接的解決
    的頭像 發表于 12-04 09:14 ?1306次閱讀

    虛擬內存的作用和原理 如何調整虛擬內存設置

    虛擬內存,也稱為虛擬內存管理或頁面文件,是計算機操作系統中的一種內存管理技術。它允許系統使用硬盤空間作為額外的RAM(隨機存取存儲器),以彌補物理內存(RAM)的不足。虛擬
    的頭像 發表于 12-04 09:13 ?1990次閱讀

    DDR5內存與DDR4內存性能差異

    DDR5內存與DDR4內存性能差異 隨著技術的發展,內存技術也在不斷進步。DDR5內存作為新一代的內存技術,相較于DDR4
    的頭像 發表于 11-29 14:58 ?1894次閱讀

    Linux內存泄露案例分析和內存管理分享

    作者:京東科技 李遵舉 一、問題 近期我們運維同事接到線上LB(負載均衡)服務內存報警,運維同事反饋說LB集群有部分機器的內存使用率超過80%,有的甚至超過90%,而且內存使用率還再不停的增長。接到
    的頭像 發表于 10-24 16:14 ?1007次閱讀
    Linux<b class='flag-5'>內存</b>泄露案例分析和<b class='flag-5'>內存</b>管理分享

    JVM xmx, xms等內存相關參數合理性設置

    的,提高內存占用(Memory Footprint)就有可能同時優化這兩個標的,這篇文章就來聊聊內存相關內容。 內存占用一般指應用運行需要的所有內存,包括堆內
    的頭像 發表于 10-10 14:42 ?1163次閱讀

    邏輯內存和物理內存的區別

    邏輯內存和物理內存是計算機系統中兩個重要的概念,它們在計算機的運行和數據處理中起著至關重要的作用。 1. 物理內存(Physical Memory) 物理內存,也稱為RAM(Rando
    的頭像 發表于 09-27 15:38 ?1615次閱讀

    線上活動 | 新思科技 2024 內存用戶大會,專注于內存設計與開發!

    方案的獨到見解。 2024年10月2日?| 上午8:00 ?(GMT+8, 中國時間) 活動亮點 專注于內存設計與開發的線上活動,匯聚內存行業的精英和新思科技的專家 提供一個專業論壇,共同分享和探討解決業界最具挑戰性和最受關注的
    發表于 09-26 14:36 ?180次閱讀
    線上活動 | 新思科技 2024 <b class='flag-5'>內存</b>用戶大會,專注于<b class='flag-5'>內存</b>設計與開發!

    內存時鐘和內存條有什么不同

    探討內存時鐘(Memory Clock)和內存條(Memory Module)的不同時,我們首先需要明確這兩個概念的基本定義和它們在計算機系統中的角色。以下是對這兩個概念的詳細解析,以及它們之間差異的比較。
    的頭像 發表于 09-04 11:45 ?3023次閱讀

    內存時鐘是什么意思

    內存時鐘是內存模塊中一個至關重要的參數,它直接關聯到內存模塊能夠工作的最高頻率。以下是對內存時鐘的詳細解析,包括其定義、作用、與內存頻率的關
    的頭像 發表于 09-04 11:45 ?2093次閱讀

    從原理聊JVM(一):染色標記和垃圾回收算法

    更好地優化自己的代碼,并解決一些潛在的性能問題。 本文及后續文章將從原理聊起,對JVM內存分配、GC、編譯等知識進行分析和總結。 1 JVM運行時內存劃分 1.1 運行時數據區域 ?
    的頭像 發表于 08-20 15:25 ?465次閱讀
    從原理聊<b class='flag-5'>JVM</b>(一):染色標記和垃圾回收算法

    操作系統的內存布局介紹

    32位操作系統的內存布局很經典,很多書籍都是以32位系統為例子去講解的。32位的系統可訪問的地址空間為4GB,用戶空間為1GB ~ 3GB,內核空間為3GB ~ 4GB。
    的頭像 發表于 08-07 15:47 ?763次閱讀
    操作系統的<b class='flag-5'>內存</b><b class='flag-5'>布局</b>介紹

    聊聊JVM如何優化

    首先應該明確的是JVM調優不是常規手段,JVM的存在本身就是為了減輕開發對于內存管理的負擔,當出現性能問題的時候第一時間考慮的是代碼邏輯與設計方案,以及是否達到依賴中間件的瓶頸,最后才是針對J
    的頭像 發表于 08-05 17:49 ?704次閱讀
    聊聊<b class='flag-5'>JVM</b>如何優化