自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JVM虛擬機(jī)整體結(jié)構(gòu)與對(duì)象內(nèi)存分配解析

云計(jì)算 虛擬化
棧是JVM重要的組成部分,每有一個(gè)新的線程都JVM都會(huì)為其在棧上分配一份內(nèi)存,線程里有棧幀,程序計(jì)數(shù)器。

[[414275]]

JVM虛擬機(jī)整體結(jié)構(gòu)解析

整體結(jié)構(gòu)介紹

  • jvm整體分為:
  • -棧方法區(qū)堆本地方法棧程序計(jì)數(shù)器

棧 Stack

棧是JVM重要的組成部分,每有一個(gè)新的線程都JVM都會(huì)為其在棧上分配一份內(nèi)存,線程里有棧幀,程序計(jì)數(shù)器。另外線程棧內(nèi)存大小決定的線程數(shù)量的多少,當(dāng)線程棧內(nèi)存大小設(shè)置的越大,則同時(shí)存在的線程數(shù)量越少,反則越大。另外在棧中最容易發(fā)生的錯(cuò)誤是StackOverflowError 棧溢出,看以下代碼:

  1. public class StackOverflowTest {  
  2.   static int count = 0; 
  3.    static void redo() {  
  4.    count++;  
  5.     redo();  
  6.     } 
  7.     public static void main(String[] args) {  
  8.      try {  
  9.    redo();  
  10.     } catch (Throwable t) {  
  11.     t.printStackTrace();  
  12.     System.out.println(count);  
  13.            }  
  14.        }  
  15.     }   
  16.      運(yùn)行結(jié)果: 
  17.      java.lang.StackOverflowError  

參數(shù)影響: -Xss 256KB(默認(rèn)1M) 設(shè)置棧大小 棧的大小會(huì)影響count 的次數(shù),-Xss設(shè)置的大小越大,count的次數(shù)也就越大,反之亦然.

棧幀結(jié)構(gòu)組成

局部變量表:主要用來(lái)保存聲明的局部變量以及方法的參數(shù)信息,局部變量表作用于為當(dāng)前方法,當(dāng)方法執(zhí)行完成后,局部變量表也會(huì)隨之刪除,釋放內(nèi)存。另外局部變量表里用來(lái)保存信息的叫做變量槽(slot)

操作數(shù)棧:顧名思義,操作數(shù)棧其本質(zhì)就是個(gè)棧,壓棧,出棧兩個(gè)操作,例如執(zhí)行a+b,先將局部變量表中的a與b分別壓入棧中,接著執(zhí)行加法操作,最終出棧。

動(dòng)態(tài)鏈接:是在程序運(yùn)行期間完成的將符號(hào)引用替換為直接引用叫動(dòng)態(tài)鏈接,既然有動(dòng)態(tài)鏈接那么自然也有靜態(tài)鏈接,部分符號(hào)引用在類加載階段(解析)的時(shí)候就轉(zhuǎn)化為直接引用,這種轉(zhuǎn)化為靜態(tài)鏈接。

方法返回地址:在方法退出(正常執(zhí)行/異常返回)后,返回方法被調(diào)用的位置。

棧結(jié)構(gòu)圖

JVM虛擬機(jī)整體結(jié)構(gòu)與對(duì)象內(nèi)存分配解析

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

程序計(jì)數(shù)器也叫PC寄存器是JVM非常重要的一個(gè)結(jié)構(gòu),是線程私有的,每個(gè)線程獨(dú)有一份,它用來(lái)保存指向下一條將被執(zhí)行指令的地址,例如當(dāng)線程被阻塞再進(jìn)行喚醒時(shí),從程序計(jì)數(shù)器讀取指令的地址,從而繼續(xù)執(zhí)行。

本地方法棧 Native Method Stack

本地方法棧主要是為了執(zhí)行native方法,保存native方法進(jìn)入?yún)^(qū)域的地址,所以本地方法棧也是線程私有的內(nèi)存區(qū)域。

方法區(qū) Method Area(元空間 Meta Space)

被所有的線程共享。方法區(qū)包含所有的class和static變量,類的方法代碼,變量名,方法名,訪問(wèn)權(quán)限,返回值,以及我們經(jīng)常說(shuō)的常量池與運(yùn)行時(shí)常量池都是在方法區(qū)的。

堆 Heap

堆是非常重要的一個(gè)區(qū)域,管理著幾乎(不是所有)所有的對(duì)象,我們常說(shuō)的垃圾回收的主要區(qū)域就是發(fā)生在這個(gè)區(qū)域。堆分為新生代(young)與老年代(Old),新生代又分為Eden與survivor區(qū),survivor分為From區(qū)與To區(qū)。這幾個(gè)區(qū)存放著java的對(duì)象,當(dāng)區(qū)內(nèi)存不夠的時(shí)候會(huì)發(fā)生GC,GC主要分為兩種,一種是minorGC(Young GC),另一種是Full GC,JVM調(diào)優(yōu)主要根據(jù)代碼調(diào)節(jié)JVM參數(shù),從而減少Full GC的次數(shù)。

堆結(jié)構(gòu)示意圖

JVM虛擬機(jī)整體結(jié)構(gòu)與對(duì)象內(nèi)存分配解析

逃逸分析

首先大家聽(tīng)得最多的就是new 出來(lái)對(duì)象是存放在堆中的,但是在上文中,所寫的是幾乎對(duì)象是存在堆中,那么為什么是幾乎呢,因?yàn)橛械膶?duì)象是存放在棧中的,是不是很不可思議,接下來(lái)來(lái)看下一段代碼。

  1. // 方法一 
  2. public Person test1() { 
  3.         Person person = new Person(); 
  4.         person.setId(1); 
  5.         return person; 
  6.         }  
  7. // 方法二       
  8. public void test2() {  
  9.          User person = new person();  
  10.          person.setId(1);  
  11.        } 

上述代碼中很顯然test1方法中的personr對(duì)象被返回了,那么這個(gè)對(duì)象就可能被其他方法進(jìn)行引用,test2方法中的personr對(duì)象,當(dāng)方法結(jié)束的時(shí)候,該對(duì)象就是一個(gè)無(wú)效對(duì)象了,不會(huì)在其他地方被進(jìn)行引用,對(duì)于這樣的對(duì)象,JVM將其分配的棧內(nèi)存里,讓其在方法結(jié)束時(shí)跟隨棧內(nèi)存一起被回收掉,減少堆內(nèi)存的回收。 JVM對(duì)于這種情況可以通過(guò)開(kāi)啟逃逸分析參數(shù)(-XX:+DoEscapeAnalysis)來(lái)優(yōu)化對(duì)象內(nèi)存分配位置,JDK7之后默認(rèn)開(kāi)啟逃逸分析,如果要關(guān)閉使用參數(shù)(-XX:-DoEscapeAnalysis)

對(duì)象內(nèi)存分配

對(duì)象內(nèi)存分配流程圖

JVM虛擬機(jī)整體結(jié)構(gòu)與對(duì)象內(nèi)存分配解析

對(duì)象棧上分配

并不是所有對(duì)象都分配在內(nèi)存,有的對(duì)象會(huì)被分配到棧上,JVM對(duì)于這種情況可以通過(guò)開(kāi)啟逃逸分析參數(shù)(-XX:+DoEscapeAnalysis)來(lái)優(yōu)化對(duì)象內(nèi)存分配位置,使其通過(guò)標(biāo)量替換優(yōu) 先分配在棧上(棧上分配),JDK7之后默認(rèn)開(kāi)啟逃逸分析,如果要關(guān)閉使用參數(shù)(-XX:-DoEscapeAnalysis)

標(biāo)量替換: 通過(guò)逃逸分析確定該對(duì)象不會(huì)被外部訪問(wèn),并且對(duì)象可以被進(jìn)一步分解時(shí),JVM不會(huì)創(chuàng)建該對(duì)象,而是將該 對(duì)象成員變量分解若干個(gè)被這個(gè)方法使用的成員變量所代替,這些代替的成員變量在棧幀或寄存器上分配空間,這樣就 不會(huì)因?yàn)闆](méi)有一大塊連續(xù)空間導(dǎo)致對(duì)象內(nèi)存不夠分配。

開(kāi)啟標(biāo)量替換參數(shù)(-XX:+EliminateAllocations),JDK7之后默認(rèn) 開(kāi)啟。

標(biāo)量與聚合量: 標(biāo)量即不可被進(jìn)一步分解的量,也可以說(shuō)是原子量,不可再分解,而JAVA的基本數(shù)據(jù)類型就是標(biāo)量(如:int,long等基本數(shù)據(jù)類型以及 reference類型等),標(biāo)量的對(duì)立就是可以被進(jìn)一步分解的量,而這種量稱之為聚合量。而在JAVA中對(duì)象就是可以被進(jìn)一 步分解的聚合量

結(jié)論:棧上分配依賴于逃逸分析和標(biāo)量替換

對(duì)象在Eden區(qū)分配

當(dāng)對(duì)象剛被創(chuàng)建的時(shí)候會(huì)被分配在eden區(qū),eden區(qū)滿了后會(huì)觸發(fā)minor gc,可能會(huì)有99%以上的對(duì)象成為垃圾被回收掉,剩余存活 的對(duì)象會(huì)被挪到為空的那塊survivor區(qū),下一次eden區(qū)滿了后又會(huì)觸發(fā)minor gc,把eden區(qū)和survivor區(qū)垃圾對(duì)象回收,把剩余存活的對(duì)象一次性挪動(dòng)到另外一塊為空的survivor區(qū),因?yàn)樾律膶?duì)象都是生命值很短的,存活時(shí)間很短,所以JVM默認(rèn)的8:1:1的比例是非常合理的一個(gè)比例值,因此我們呢應(yīng)該讓eden區(qū)盡量的大,survivor區(qū)夠用即可,

JVM默認(rèn)有這個(gè)參數(shù)-XX:+UseAdaptiveSizePolicy(默認(rèn)開(kāi)啟),會(huì)導(dǎo)致這個(gè)8:1:1比例自動(dòng)變化.

如果不想這個(gè)比例有變 化可以設(shè)置參數(shù)

-XX:-UseAdaptiveSizePolicy

當(dāng)Eden區(qū)內(nèi)存不夠用了會(huì)出現(xiàn)聲明狀況?

如果因?yàn)榻o新對(duì)象分配內(nèi)存的時(shí)候eden區(qū)內(nèi)存幾乎已經(jīng)被分配完了,bane當(dāng)Eden區(qū)沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次Minor GC,GC期間虛擬機(jī)又發(fā)現(xiàn)新對(duì)象無(wú)法存入Survior空間,所以只好把新生代的對(duì)象提前轉(zhuǎn)移到老年代中去,老年代上的空間足夠存放新對(duì)象,所以不會(huì)出現(xiàn)Full GC。執(zhí)行Minor GC后,后面分配的對(duì)象如果能夠存在eden區(qū)的話,還是會(huì)在eden區(qū)分配內(nèi)存。

大對(duì)象直接進(jìn)入老年代

大對(duì)象就是需要大量連續(xù)內(nèi)存空間的對(duì)象(比如:字符串、數(shù)組)。JVM參數(shù)

-XX:PretenureSizeThreshold 可以設(shè)置大 對(duì)象的大小,如果對(duì)象超過(guò)設(shè)置大小會(huì)直接進(jìn)入老年代,不會(huì)進(jìn)入年輕代,這個(gè)參數(shù)只在 Serial 和ParNew兩個(gè)收集器下 有效(關(guān)于收集器日后再講)。

比如設(shè)置JVM參數(shù):

-XX:PretenureSizeThreshold=1000000 (單位是字節(jié)) -XX:+UseSerialGC ,再執(zhí)行下帶有大對(duì)象的程序會(huì)發(fā)現(xiàn)大對(duì)象直接進(jìn)了老年代

這樣做的好處?

為了避免為大對(duì)象分配內(nèi)存時(shí)的復(fù)制操作而降低效率。

長(zhǎng)期存活的對(duì)象將進(jìn)入老年代

既然虛擬機(jī)采用了分代收集的思想來(lái)管理內(nèi)存,那么內(nèi)存回收時(shí)就必須能識(shí)別哪些對(duì)象應(yīng)放在新生代,哪些對(duì)象應(yīng)放在 老年代中。為了做到這一點(diǎn),虛擬機(jī)給每個(gè)對(duì)象一個(gè)對(duì)象年齡(Age)計(jì)數(shù)器。 如果對(duì)象在 Eden 出生并經(jīng)過(guò)第一次 Minor GC 后仍然能夠存活,并且能被 Survivor 容納的話,將被移動(dòng)到 Survivor 空間中,并將對(duì)象年齡設(shè)為1。對(duì)象在 Survivor 中每熬過(guò)一次 MinorGC,年齡就增加1歲,當(dāng)它的年齡增加到一定程度(默認(rèn)為15歲,CMS收集器默認(rèn)6歲,不同的垃圾收集器會(huì)略微有點(diǎn)不同),就會(huì)被晉升到老年代中。對(duì)象晉升到老年代

的年齡閾值.

JVM參數(shù)設(shè)置 -XX:MaxTenuringThreshold 。

對(duì)象動(dòng)態(tài)年齡判斷

當(dāng)前放對(duì)象的Survivor區(qū)域里(其中一塊區(qū)域,放對(duì)象的那塊s區(qū)),一批對(duì)象的總大小大于這塊Survivor區(qū)域內(nèi)存大小的

50%(-XX:TargetSurvivorRatio可以指定),那么此時(shí)大于等于這批對(duì)象年齡最大值的對(duì)象,就可以直接進(jìn)入老年代了,

例如Survivor區(qū)域里現(xiàn)在有一批對(duì)象,年齡1+年齡2+年齡n的多個(gè)年齡對(duì)象總和超過(guò)了Survivor區(qū)域的50%,此時(shí)就會(huì)

把年齡n(含)以上的對(duì)象都放入老年代。這個(gè)規(guī)則其實(shí)是希望那些可能是長(zhǎng)期存活的對(duì)象,盡早進(jìn)入老年代。對(duì)象動(dòng)態(tài)年

齡判斷機(jī)制一般是在minor gc之后觸發(fā)的。

老年代空間分配擔(dān)保機(jī)制

年輕代每次minor gc之前JVM都會(huì)計(jì)算下老年代剩余可用空間 如果這個(gè)可用空間小于年輕代里現(xiàn)有的所有對(duì)象大小之和(包括垃圾對(duì)象) 就會(huì)看一個(gè)“

-XX:-HandlePromotionFailure”(jdk1.8默認(rèn)就設(shè)置了)的參數(shù)是否設(shè)置了 如果有這個(gè)參數(shù),就會(huì)看看老年代的可用內(nèi)存大小,是否大于之前每一次minor gc后進(jìn)入老年代的對(duì)象的平均大小。 如果上一步結(jié)果是小于或者之前說(shuō)的參數(shù)沒(méi)有設(shè)置,那么就會(huì)觸發(fā)一次Full gc,對(duì)老年代和年輕代一起回收一次垃圾, 如果回收完還是沒(méi)有足夠空間存放新的對(duì)象就會(huì)發(fā)生"OOM" 當(dāng)然,如果minor gc之后剩余存活的需要挪動(dòng)到老年代的對(duì)象大小還是大于老年代可用空間,那么也會(huì)觸發(fā)full gc,full gc完之后如果還是沒(méi)有空間放minor gc之后的存活對(duì)象,則也會(huì)發(fā)生“OOM.

總結(jié)

  1. 運(yùn)行時(shí)數(shù)據(jù)區(qū)主要由堆、棧、程序計(jì)數(shù)器、方法區(qū)、本地方法棧
  2. 線程私有的區(qū)域:線程棧、程序計(jì)數(shù)器、本地方法棧,線程共享的區(qū)域:堆、方法區(qū)。
  3. 堆分為細(xì)分為新生代(Eden、survivor(From、To)默認(rèn)比例8:1:1)、老年代
  4. 對(duì)象不全都是在堆中,經(jīng)過(guò)發(fā)生逃逸符合條件的對(duì)象在棧中
  5. JVM整體結(jié)構(gòu)圖如下
JVM虛擬機(jī)整體結(jié)構(gòu)與對(duì)象內(nèi)存分配解析

 

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2011-11-30 14:12:05

JavaJVM虛擬機(jī)

2010-09-25 15:13:40

JVMJava虛擬機(jī)

2010-09-25 15:59:54

JVM虛擬機(jī)

2009-06-04 16:27:39

Java虛擬機(jī)JVMGC

2010-09-17 15:12:57

JVMJava虛擬機(jī)

2018-04-08 08:45:53

對(duì)象內(nèi)存策略

2011-05-26 15:41:25

java虛擬機(jī)

2020-05-08 16:55:48

Java虛擬機(jī)JVM

2011-06-22 13:35:55

JVM

2010-09-25 16:12:45

JVM虛擬機(jī)

2012-01-11 10:45:57

JavaJVM

2024-02-21 07:40:17

JVM內(nèi)存虛擬機(jī)

2011-01-26 11:01:37

虛擬機(jī)負(fù)載管理資源分配

2011-12-28 13:24:47

JavaJVM

2010-02-04 10:05:28

Dalvik虛擬機(jī)

2017-03-17 09:48:09

DVMJVMAndroid

2014-04-09 14:15:21

虛擬機(jī)虛擬機(jī)資源

2011-12-28 13:38:00

JavaJVM

2011-12-20 10:43:21

Java

2017-09-20 08:48:09

JVM內(nèi)存結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)