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

java虛擬機(jī)深入學(xué)習(xí)之內(nèi)存管理機(jī)制

 更新時(shí)間:2018年11月13日 09:15:49   作者:blueskyli  
java虛擬機(jī)在程序運(yùn)行時(shí)將內(nèi)存劃分為多個(gè)區(qū)域,每個(gè)區(qū)域作用,生命周期各不相同,下面這篇文章主要給大家介紹了關(guān)于java虛擬機(jī)深入學(xué)習(xí)之內(nèi)存管理機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

前面說過了類的加載機(jī)制,里面講到了類的初始化中時(shí)用到了一部分內(nèi)存管理的知識,這里讓我們來看下Java虛擬機(jī)是如何管理內(nèi)存的。

先讓我們來看張圖

有些文章中對線程隔離區(qū)還稱之為線程獨(dú)占區(qū),其實(shí)是一個(gè)意思了。下面讓我們來詳細(xì)介紹下這五部分;

運(yùn)行時(shí)數(shù)據(jù)區(qū)

Java虛擬機(jī)在執(zhí)行Java程序的過程中會(huì)把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域,這些區(qū)域都擁有自己的用途,并隨著JVM進(jìn)程的啟動(dòng)或者用戶線程的啟動(dòng)和結(jié)束建立和銷毀。

先讓我們了解下進(jìn)程和線程的區(qū)別:

進(jìn)程是資源分配的最小單位,線程是程序執(zhí)行的最小單位。

進(jìn)程有自己的獨(dú)立地址空間,每啟動(dòng)一個(gè)進(jìn)程,系統(tǒng)就會(huì)為它分配一個(gè)地址空間、建立數(shù)據(jù)表來維護(hù)代碼段、堆棧段和數(shù)據(jù)段,這種操作非常昂貴。而線程是共享進(jìn)程中的數(shù)據(jù)的,使用相同的地址空間,因此CPU切換一個(gè)線程的花費(fèi)要比進(jìn)程小很多,同時(shí)創(chuàng)建一個(gè)線程的開銷也要比進(jìn)程小很多。

同一個(gè)進(jìn)程中可以包括多個(gè)線程,并且線程共享整個(gè)進(jìn)程的資源(寄存器、堆棧、上下文),一個(gè)進(jìn)程至少包含一個(gè)線程。線程之間的通信更加方便,同一進(jìn)程下的線程共享全局變量、靜態(tài)變量等數(shù)據(jù),而進(jìn)程之間的通信則需要以通信的方式(IPC)進(jìn)行。

這里引用在知乎中某位同學(xué)的解釋,

進(jìn)程的顆粒度太大,每次都要有上下的調(diào)入,保存,調(diào)出。如果我們把進(jìn)程比喻為一個(gè)運(yùn)行在電腦上的軟件,那么一個(gè)軟件的執(zhí)行不可能是一條邏輯執(zhí)行的,必定有多個(gè)分支和多個(gè)程序段,就好比要實(shí)現(xiàn)程序A,實(shí)際分成 a,b,c等多個(gè)塊組合而成。那么這里具體的執(zhí)行就可能變成:

程序A得到CPU =》CPU加載上下文,開始執(zhí)行程序A的a小段,然后執(zhí)行A的b小段,然后再執(zhí)行A的c小段,最后CPU保存A的上下文。

這里a,b,c的執(zhí)行是共享了A的上下文,CPU在執(zhí)行的時(shí)候沒有進(jìn)行上下文切換的。

看到這里是不是對線程共享和線程隔離區(qū)有了一個(gè)更深次的理解??梢岳斫鉃榉椒▍^(qū)和堆是分配給進(jìn)程的,也就是線程共享區(qū),而棧和程序計(jì)數(shù)器則是分配給每個(gè)獨(dú)立線程的。

在SUN公司的HotSpot虛擬機(jī)中將java虛擬機(jī)棧和本地方法棧合二為一了

程序計(jì)數(shù)器(Program Counter Register)

程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它可以看成是當(dāng)前線程所執(zhí)行字節(jié)碼的行號指示器。在計(jì)算機(jī)中,其實(shí)程序計(jì)數(shù)器就是一個(gè)寄存器,依據(jù)不同計(jì)算機(jī)細(xì)節(jié)的差異,它可以存放當(dāng)前正在被執(zhí)行的指令,也可以放下一個(gè)被執(zhí)行的指令。

在虛擬機(jī)的概念模型中,字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令的。

由于Java虛擬機(jī)的多線程是通過線程輪詢切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的,在任何一個(gè)確定的時(shí)候,一個(gè)處理器都只會(huì)執(zhí)行一條線程中的指令,因此為了線程切換之后能過恢復(fù)到正確的執(zhí)行位置,每條線程都需要擁有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間的計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ),所以程序計(jì)數(shù)器是線程私有的內(nèi)存,也就是它屬于線程隔離區(qū)的。

如果線程執(zhí)行的是一個(gè)Java方法,這個(gè)計(jì)數(shù)器記錄的就是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址;如果正在執(zhí)行的是Native方法,那么這個(gè)計(jì)數(shù)器的值就是(Undefined)。

此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

Java虛擬機(jī)棧

Java虛擬機(jī)棧(Java Virtual Machine Stack)也是線程私有的,即他的生命周期和線程相同。

在Java中,JVM中的棧記錄了線程的方法調(diào)用,每個(gè)線程擁有一個(gè)棧,在某個(gè)線程的運(yùn)行過程中,如果有新的方法調(diào)用,那么該線程對應(yīng)的棧就會(huì)增加一個(gè)存儲(chǔ)單元,即棧針(Stack Frame)。

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

當(dāng)被調(diào)用方法運(yùn)行結(jié)束時(shí),該方法對應(yīng)的幀將被刪除,參數(shù)和局部變量所占據(jù)的空間也隨之釋放。線程回到原方法,繼續(xù)執(zhí)行。當(dāng)所有的棧都清空時(shí),程序也隨之運(yùn)行結(jié)束。

我們經(jīng)常說的棧內(nèi)存其實(shí)就是現(xiàn)在講的虛擬機(jī)棧,或者說是虛擬機(jī)棧中局部變量表部分。

局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同于對象本身,可能是指向?qū)ο笃鹗嘉恢玫囊弥羔槪部赡苁侵赶蛞粋€(gè)代表對象的句柄或其他與此對象相關(guān)的位置,引用所指向的對象保存在堆中(引用可能為Null,即不指向任何對象))和returnAddress類型(指向了一條字節(jié)碼指令的地址)。

其中64位長度的long和double類型的數(shù)據(jù)會(huì)占用2個(gè)局部變量空間(Slot),其余數(shù)據(jù)類型只占用1個(gè)。局部變量表所需要的內(nèi)存空間在編譯時(shí)期完成分配。當(dāng)進(jìn)入一個(gè)方法時(shí),這個(gè)方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運(yùn)行期間不會(huì)改變局部變量表的大小。

異常有兩種

1,線程請求的棧深度大于虛擬機(jī)所允許的深度將拋出StackOverflowError異常 (遞歸調(diào)用)

2,如果虛擬機(jī)可以動(dòng)態(tài)擴(kuò)展,如果擴(kuò)展時(shí)已經(jīng)無法申請到足夠的內(nèi)存就會(huì)拋出OutOfMemeoryError異常。

List list=new ArrayList();
  for(;;){
   int[] tmp=new int[1000000];
   list.add(tmp);
  }

本地方法棧

本地方法棧(Native Method Stack)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的。他們之間的區(qū)別就是Java虛擬機(jī)棧是位虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧為位虛擬機(jī)使用到的Native方法服務(wù)。

其實(shí)虛擬機(jī)規(guī)范中對本地方發(fā)棧中方法所使用的語言、使用方式以及數(shù)據(jù)結(jié)構(gòu)都沒有強(qiáng)制規(guī)定,因此具體的虛擬機(jī)可以自由地實(shí)現(xiàn)它。甚至在有的虛擬機(jī)(如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一。與虛擬機(jī)棧一樣,本地方法棧區(qū)域也會(huì)拋出StackOverflowError和OutOfMemory異常。

Java堆

對于大多數(shù)應(yīng)用來說,Java堆(Java Heap)是Java虛擬機(jī)管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊數(shù)據(jù)區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,這一內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配內(nèi)存。但是隨著JIT編譯器的發(fā)展與逃逸分析技術(shù)逐漸成熟,棧上分配、標(biāo)量替換優(yōu)化技術(shù)將會(huì)導(dǎo)致一些微妙的變化發(fā)生,所有的對象都分配在堆上也逐漸變得不是那么“絕對”。

堆中可細(xì)分為新生代和老年代,在細(xì)分可以分為Eden空間、Form Survivor空間、to Survivor空間。

Java堆是垃圾收集器管理的主要區(qū)域,因此很多時(shí)候也被稱為“GC堆”。

根據(jù)Java虛擬機(jī)規(guī)范規(guī)定,Java堆可以處于物理上不連續(xù)的內(nèi)存中,即只要邏輯上是連續(xù)的即可,就像我們的磁盤空間一樣。在實(shí)現(xiàn)時(shí),可以固定大小也是可擴(kuò)展的。主流的虛擬機(jī)都是按照可擴(kuò)展來實(shí)現(xiàn)的(通過-Xmx和-Xms來控制)。如果在堆中沒有內(nèi)存可分配,并且堆也無法繼續(xù)擴(kuò)展時(shí),將會(huì)拋出OutOfMemortError異常。

Java的普通對象存活在堆中,與棧不同,堆的空間不會(huì)隨著方法調(diào)用結(jié)束而清空。因此,在某個(gè)方法中創(chuàng)建的對象,可以在方法調(diào)用結(jié)束之后,繼續(xù)存在堆中。這帶來的一個(gè)問題是,如果我們不斷的創(chuàng)建新的對象,內(nèi)存控件將會(huì)最終消耗殆盡。

方法區(qū)

方法區(qū)(Method Area)與Java堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已經(jīng)被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯之后的代碼等數(shù)據(jù)。雖然Java虛擬機(jī)將其描述為堆的一個(gè)邏輯部分,但它卻有一個(gè)別名叫做Non-Heap(非堆)。目的是與Java堆區(qū)分開來。(以前很多人把方法區(qū)稱為永久代,現(xiàn)在JDK1.8中已經(jīng)用元數(shù)據(jù)區(qū)域取代了永久代)。

運(yùn)行時(shí)常量池

運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池。用于存放編譯期生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進(jìn)入到方法區(qū)的運(yùn)行時(shí)常量池中存放。并非預(yù)置入Class文件中常量池的內(nèi)容才進(jìn)入方法運(yùn)行時(shí)常量池,運(yùn)行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用得比較多的便是String類的intern()方法。

public class Test
{
 public static void main(String[] args)
 {
  String s1="hello china";//字節(jié)碼常量
  String s2="hello china";
  String s3=new String("hello china");

  System.out.println(s1 == s2);
  System.out.println(s1 == s3);
  System.out.println(s1 == s3.intern());//運(yùn)行時(shí)常量 intern 是個(gè)native方法
 }
}

當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí),拋出OutOfMemoryError

注:JDK8之前,方法區(qū)由永久代實(shí)現(xiàn),主要存放類的信息、常量池、方法數(shù)據(jù)、方法代碼等;JDK8之后,取消了永久代,提出了元空間,并且常量池、靜態(tài)成員變量等遷移到了堆中;元空間不在虛擬機(jī)內(nèi)存中,而是放在本地內(nèi)存中。

直接內(nèi)存

由于直接內(nèi)存(Direct Memory)并不是java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域,但是這部分也被頻繁使用,而且也可能導(dǎo)致內(nèi)存溢出異常出現(xiàn),所以也放到這一部分進(jìn)行簡介。

先讓,本機(jī)直接內(nèi)存的分配不會(huì)受到Java堆大小的限制,但是肯定還是會(huì)受到本機(jī)總內(nèi)存大小以及處理器尋址空間的限制。管理員在配置虛擬機(jī)參數(shù)時(shí),會(huì)根據(jù)實(shí)際內(nèi)存設(shè)置-Xmx等參數(shù)信息,但經(jīng)常會(huì)忽略直接內(nèi)存,使得各個(gè)內(nèi)存區(qū)域總和大于物理內(nèi)存限制(包含物理的和操作系統(tǒng)級的限制),從而導(dǎo)致動(dòng)態(tài)擴(kuò)展時(shí)出現(xiàn)OutOfMemoryError異常。

JDK1.4加入了NIO,引入一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲(chǔ)在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作。因此避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù),提高了性能。 

Object obj = new Object();

Object obj將會(huì)反映到虛擬機(jī)棧中(reference類型)

new Object()將會(huì)反映到Java堆中

此類的對象類型、父類、實(shí)現(xiàn)的接口、方法等信息數(shù)據(jù),將反映到方法區(qū)中

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Spring boot+beetl+i18n國際化處理的方法

    Spring boot+beetl+i18n國際化處理的方法

    這篇文章主要介紹了Spring boot+beetl+i18n國際化處理的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Java super關(guān)鍵字調(diào)用父類過程解析

    Java super關(guān)鍵字調(diào)用父類過程解析

    這篇文章主要介紹了Java super關(guān)鍵字調(diào)用父類過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Java JVM原理與調(diào)優(yōu)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java JVM原理與調(diào)優(yōu)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來的計(jì)算機(jī),是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的。下面通過本文給大家介紹jvm原理與調(diào)優(yōu)相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧
    2017-04-04
  • mybatis相同的sql查詢第二次查不出結(jié)果問題

    mybatis相同的sql查詢第二次查不出結(jié)果問題

    這篇文章主要介紹了mybatis相同的sql查詢第二次查不出結(jié)果問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • java 如何計(jì)算同比增長工具類

    java 如何計(jì)算同比增長工具類

    這篇文章主要介紹了java 如何計(jì)算同比增長工具類的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java 如何優(yōu)雅的拷貝對象屬性

    Java 如何優(yōu)雅的拷貝對象屬性

    這篇文章主要介紹了Java 如何優(yōu)雅的拷貝對象屬性,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-11-11
  • java如何實(shí)現(xiàn)socket連接方法封裝

    java如何實(shí)現(xiàn)socket連接方法封裝

    這篇文章主要介紹了java實(shí)現(xiàn)socket連接方法封裝教程,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java?8?中?Function?接口使用方法介紹

    Java?8?中?Function?接口使用方法介紹

    這篇文章主要介紹了Java?8中?Function接口使用方法介紹,Java8中提供了一個(gè)函數(shù)式接口Function,這個(gè)接口表示對一個(gè)參數(shù)做一些操作然后返回操作之后的值
    2022-06-06
  • 詳解MyBatisPlus邏輯刪除與唯一索引沖突問題

    詳解MyBatisPlus邏輯刪除與唯一索引沖突問題

    這篇文章主要介紹了詳解MyBatisPlus邏輯刪除與唯一索引沖突問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java 中的內(nèi)存映射 mmap

    Java 中的內(nèi)存映射 mmap

    這篇文章主要介紹了Java 中的內(nèi)存映射,mmap 是一種內(nèi)存映射文件的方法,即將一個(gè)文件映射到進(jìn)程的地址空間,實(shí)現(xiàn)文件磁盤地址和一段進(jìn)程虛擬地址的映射,下面來看看詳細(xì)內(nèi)容,需要的朋友可以參考一下
    2021-11-11

最新評論