欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Java內(nèi)存區(qū)域劃分和內(nèi)存分配策略

 更新時(shí)間:2020年05月17日 14:14:45   作者:zycxnanwang  
這篇文章主要介紹了淺談Java內(nèi)存區(qū)域劃分和內(nèi)存分配策略,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

如果不知道,類的靜態(tài)變量存儲(chǔ)在那? 方法的局部變量存儲(chǔ)在那? 趕快收藏

Java內(nèi)存區(qū)域主要可以分為共享內(nèi)存,堆、方法區(qū)和線程私有內(nèi)存,虛擬機(jī)棧、本地方法棧和程序計(jì)數(shù)器。如下圖所示,本文將詳細(xì)講述各個(gè)區(qū)域,同時(shí)也會(huì)講述創(chuàng)建對(duì)象過程,內(nèi)存分配策略, 和對(duì)象訪問定位原理。覺得寫得好的,可以點(diǎn)個(gè)收藏,絕對(duì)不虧。

Java內(nèi)存區(qū)域

程序計(jì)數(shù)器

程序計(jì)數(shù)器,可以看作程序當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)指示器。字節(jié)碼解釋器工作時(shí)就是通過改變計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理都需要依賴計(jì)數(shù)器完成。線程執(zhí)行Java方法時(shí),記錄其正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址,線程執(zhí)行Native方法時(shí),計(jì)數(shù)器記錄為空。程序計(jì)數(shù)器時(shí)唯一在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況區(qū)域。

理論可知,線程是通過輪流獲取CPU執(zhí)行時(shí)間以實(shí)現(xiàn)多線程的并發(fā)。為了暫停的線程下一次獲得CPU執(zhí)行時(shí)間,能正常運(yùn)行,每一個(gè)線程內(nèi)部都需要維護(hù)一個(gè)程序計(jì)數(shù)器,用來記住暫停線程暫停的位置。

注意:光理論是不夠的,在此送大家一套2020最新Java架構(gòu)實(shí)戰(zhàn)教程+大廠面試寶典,點(diǎn)擊此處 進(jìn)來獲取 一起交流進(jìn)步哦!

Java虛擬機(jī)棧

Java虛擬機(jī)棧同程序計(jì)數(shù)器一樣,也是線程私有的,虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量表,操作數(shù)棧、動(dòng)態(tài)鏈接和方法出入口等信息。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)中入棧到出棧的過程。

本地方法棧

與虛擬機(jī)棧相似。虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法服務(wù),而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)。

Java堆

所有線程共享的一塊內(nèi)存區(qū)域。Java虛擬機(jī)所管理的內(nèi)存中最大的一塊,因?yàn)樵搩?nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例。幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存,同時(shí)堆也是垃圾收集器管理的主要區(qū)域。因此很多時(shí)候被稱為"GC堆"

方法區(qū)

和堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、和編譯器編譯后的代碼(也就是存儲(chǔ)字節(jié)碼文件.class)等數(shù)據(jù)。

方法區(qū)中有一個(gè)運(yùn)行時(shí)常量池,編譯后期生成的各種字面量和符號(hào)引用,存放在字節(jié)碼文件中的常量池中。當(dāng)類加載進(jìn)入方法區(qū)時(shí),就會(huì)把該常量池中的內(nèi)容放入方法區(qū)中的運(yùn)行時(shí)常量池。此外也可以在程序運(yùn)行期間,將新的常量放入運(yùn)行時(shí)常量池,比如String.intern()方法,該方法先從運(yùn)行時(shí)常量池中查找是否有該值,如果有,則返回該值的引用,否則將該值加入運(yùn)行時(shí)常量池。

實(shí)例詳講

class Demo1_Car{
  public static void main(String[] args) {
    Car c1 = new Car();
    //調(diào)用屬性并賦值
    c1.color = "red";
    c1.num = 8;
    //調(diào)用行為
    c1.run();
    Car c2 = new Car();
    c2.color = "black";
    c2.num = 4;
    c2.run();
  }
}
Class Car{
  String color;
  int num;
  public void run() {
  	System.out.println(color + ".." + num);
	}
}

  • 首先運(yùn)行程序,Demo1_car.java就會(huì)變?yōu)镈emo1_car.class,Demo1_car.class加入方法區(qū),檢查是否字節(jié)碼文件常量池中是否有常量值,如果有,那么就加入運(yùn)行時(shí)常量池。
  • 遇到main方法,創(chuàng)建一個(gè)棧幀,入虛擬機(jī)棧,然后開始運(yùn)行main方法中的程序。
  • Car c1 = new Car(), 第一次遇到Car這個(gè)類,所以將Car.java編譯為Car.class文件,然后加入方法區(qū).然后new Car(),在堆中創(chuàng)建一塊區(qū)域,用于存放創(chuàng)建出來的實(shí)例對(duì)象,地址為0X001.其中有兩個(gè)屬性值color和num。默認(rèn)值是null和 0
  • 然后通過c1這個(gè)引用變量去設(shè)置color和num的值,調(diào)用run方法,然后會(huì)創(chuàng)建一個(gè)棧幀,用來存儲(chǔ)run方法中的局部變量等。run 方法中就打印了一句話,結(jié)束之后,該棧幀出虛擬機(jī)棧。又只剩下main方法這個(gè)棧幀。
  • 接著又創(chuàng)建了一個(gè)Car對(duì)象,所以又在堆中開辟了一塊內(nèi)存,之后就是跟之前的步驟一樣了。

創(chuàng)建對(duì)象過程

虛擬機(jī)在遇到一條new指令時(shí),會(huì)首先檢查這個(gè)指令的參數(shù)是否可以在方法區(qū)中定位到一個(gè)類的符號(hào)引用,并且檢查這個(gè)符號(hào)引用所代表的類是否已經(jīng)被加載,解析和初始化過。如果沒有,則必須先執(zhí)行類加載過程.

類加載完之后,需要為對(duì)象分配內(nèi)存,有兩種分配內(nèi)存的方法

  • 指針碰撞法(要求堆內(nèi)存規(guī)整)

Java堆中空閑內(nèi)存和已使用內(nèi)存分別存放在堆的兩邊,中間存放一個(gè)指針作為分界點(diǎn)的指示器,在為對(duì)象分配內(nèi)存時(shí)只需要將指針向空閑區(qū)域移動(dòng)創(chuàng)建對(duì)象所需要的內(nèi)存大小即可。

  • 空閑列表法

如果堆內(nèi)存中已使用內(nèi)存區(qū)域和空閑區(qū)域相互交錯(cuò),此時(shí)虛擬機(jī)需要維護(hù)一個(gè)列表,記錄哪些內(nèi)存塊是可用的,在分配時(shí)從列表中找到一塊足夠大的內(nèi)存區(qū)域劃分給對(duì)象實(shí)例并更新列表上的記錄。

多線程情況下,線程同時(shí)分配內(nèi)存可能會(huì)造成沖突,比如使用指針碰撞法,線程A正在分配內(nèi)存,還沒有改變指針指向,線程B,又同時(shí)使用原來的指針進(jìn)行內(nèi)存分配。防止沖突有兩種方法

  • CAS操作:虛擬機(jī)采用CAS操作,加上失敗重試的方式保證內(nèi)存分配的原子性
  • 本地線程分配緩沖(TLAB):預(yù)先為線程分配一部分堆內(nèi)存空間(線程私有,所以不存在同步問題)用于對(duì)象實(shí)例的內(nèi)存分配。只有當(dāng)TLAB用完,需要分配新的TLAB時(shí),才需要進(jìn)行同步操作。

內(nèi)存分配完之后,虛擬機(jī)需要將分配到的內(nèi)存空間均初始化為零值(不包括對(duì)象頭)。在虛擬機(jī)中,執(zhí)行完new指令后會(huì)接著執(zhí)行方法,把對(duì)象按照程序員的意愿進(jìn)行初始化,這樣一個(gè)真正可用的對(duì)象才算完全產(chǎn)生出來

對(duì)象在內(nèi)存中的布局

對(duì)象在內(nèi)存中的布局如下圖所示,分為對(duì)象頭、實(shí)例數(shù)據(jù)、對(duì)齊填充
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳

mark Word, 用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼、GC分代年齡以及鎖狀態(tài)標(biāo)志等。類型指針,即對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。

  • 實(shí)例數(shù)據(jù)

對(duì)象真正存儲(chǔ)的有效信息,也是程序代碼中所定義的各種類型的字段內(nèi)容。

  • 對(duì)齊填充

并非必然存在,僅僅起著占位符的作用。

對(duì)象的訪問定位

Java程序需要通過棧上的reference數(shù)據(jù)來操作堆上的具體對(duì)象。共有兩種策略進(jìn)行對(duì)象的訪問定位

  • 句柄訪問

Java堆中劃分出一塊內(nèi)存來作為句柄池,reference中存儲(chǔ)的是對(duì)象的句柄地址,而句柄中包含了對(duì)象實(shí)例數(shù)據(jù)與類型數(shù)據(jù)各自的具體地址信息,需要兩次尋址。

  • 直接指針訪問

Java堆中對(duì)象的布局中需要考慮如何放置訪問類型數(shù)據(jù)的相關(guān)信息,而reference中存儲(chǔ)的直接就是對(duì)象地址。

使用句柄訪問的最大好處就是reference中存儲(chǔ)的是穩(wěn)定的句柄地址,在對(duì)象被移動(dòng)(垃圾收集時(shí)移動(dòng)對(duì)象是非常普遍的行為)時(shí)只會(huì)改變句柄中實(shí)例數(shù)據(jù)指針,而reference本身不需要修改。

問題

只需要記住一件事,就是Java對(duì)象的內(nèi)存分配均是在堆中進(jìn)行的。所以對(duì)象都存儲(chǔ)在堆中。

但是有人可能會(huì)懷疑方法的臨時(shí)變量不是存儲(chǔ)在虛擬機(jī)棧中嗎?這里我要解釋一下,虛擬機(jī)棧維護(hù)了一個(gè)局部變量表,表中存儲(chǔ)的是對(duì)象的引用,而真正存儲(chǔ)對(duì)象的地方在堆,如果局部變量都在堆里分配,那么虛擬機(jī)棧早爆滿了

同樣類的靜態(tài)變量,有人又會(huì)懷疑在方法區(qū)中存儲(chǔ)。其實(shí)不是的,方法區(qū)只存儲(chǔ)引用,具體對(duì)象是存儲(chǔ)在堆中的,具體實(shí)現(xiàn)可以發(fā)現(xiàn),類靜態(tài)對(duì)象是與class對(duì)象一起分配的內(nèi)存。

參考

深入理解java虛擬機(jī)

到此這篇關(guān)于淺談Java內(nèi)存區(qū)域劃分和內(nèi)存分配策略的文章就介紹到這了,更多相關(guān)Java內(nèi)存區(qū)域劃分和內(nèi)存分配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA實(shí)現(xiàn)Date日期加一天具體方法

    JAVA實(shí)現(xiàn)Date日期加一天具體方法

    這篇文章主要給大家介紹了關(guān)于JAVA實(shí)現(xiàn)Date日期加一天的相關(guān)資料,因?yàn)樵陧?xiàng)目中遇到了需要將日期進(jìn)行加減一些天數(shù)的操作,文中給出了簡單的代碼示例,需要的朋友可以參考下
    2023-07-07
  • Java實(shí)現(xiàn)直接插入排序和折半插入排序算法示例

    Java實(shí)現(xiàn)直接插入排序和折半插入排序算法示例

    這篇文章主要介紹了Java實(shí)現(xiàn)直接插入排序和折半插入排序算法示例,文中對(duì)算法的思想和時(shí)間復(fù)雜度都有簡單的講解,需要的朋友可以參考下
    2016-04-04
  • 如何使用JaCoCo分析java單元測試覆蓋率

    如何使用JaCoCo分析java單元測試覆蓋率

    在做單元測試時(shí),代碼覆蓋率常常被拿來作為衡量測試好壞的指標(biāo),甚至,用代碼覆蓋率來考核測試任務(wù)完成情況,比如,代碼覆蓋率必須達(dá)到80%或 90%。于是乎,測試人員費(fèi)盡心思設(shè)計(jì)案例覆蓋代碼。下面我們來學(xué)習(xí)一下吧
    2019-06-06
  • Spring MVC的優(yōu)點(diǎn)與核心接口_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Spring MVC的優(yōu)點(diǎn)與核心接口_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Spring MVC的優(yōu)點(diǎn)與核心接口,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 淺談Java文件被執(zhí)行的歷程

    淺談Java文件被執(zhí)行的歷程

    學(xué)習(xí)java以來,都是以語法,類庫入手,最基本的也是最基礎(chǔ)的java編譯過程往往被我遺忘,先解釋一下學(xué)習(xí)java第一課時(shí),都聽到過的一句話,“java是半解釋語言”。什么是半解釋語言。本文將介紹Java文件被執(zhí)行的歷程。
    2021-06-06
  • Spring?Boot配置文件的語法規(guī)則詳解(properties和yml)

    Spring?Boot配置文件的語法規(guī)則詳解(properties和yml)

    這篇文章主要介紹了Spring?Boot配置文件的語法規(guī)則,主要介紹兩種配置文件的語法和格式,properties和yml,對(duì)于配置文件也有獨(dú)立的文件夾存放,主要用來存放一些需要經(jīng)過變動(dòng)的數(shù)據(jù)(變量值),感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • Java設(shè)計(jì)模式之責(zé)任鏈模式的概念、實(shí)現(xiàn)以及netty中的責(zé)任鏈模式

    Java設(shè)計(jì)模式之責(zé)任鏈模式的概念、實(shí)現(xiàn)以及netty中的責(zé)任鏈模式

    這篇文章主要給大家介紹了關(guān)于設(shè)計(jì)模式之責(zé)任鏈模式的概念、實(shí)現(xiàn)以及netty中的責(zé)任鏈模式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之高級(jí)排序

    帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之高級(jí)排序

    這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之高級(jí)排序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Java的單例設(shè)計(jì)模式詳解

    Java的單例設(shè)計(jì)模式詳解

    今天小編就為大家分享一篇關(guān)于Java的單例設(shè)計(jì)模式詳解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • SpringBoot如何返回頁面的實(shí)現(xiàn)方法

    SpringBoot如何返回頁面的實(shí)現(xiàn)方法

    SpringBoot中使用Controller和頁面的結(jié)合能夠很好地實(shí)現(xiàn)用戶的功能及頁面數(shù)據(jù)的傳遞。本文介紹了如何實(shí)現(xiàn)頁面的返回以及這里面所包含的坑,感興趣的可以了解一下
    2021-07-07

最新評(píng)論