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

1.JDK1.7內(nèi)存模型-運(yùn)行時(shí)數(shù)據(jù)區(qū)域

根據(jù)《Java虛擬機(jī)規(guī)范(Java SE 7 版)》規(guī)定,Java虛擬機(jī)所管理的內(nèi)存如下圖所示


運(yùn)行時(shí)數(shù)據(jù)區(qū)模型
  • 1.堆:存放所有new出來的東西序苏,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,垃圾收集器管理的主要區(qū)域讨跟。線程共享
  • 2.方法區(qū):存儲(chǔ)被虛擬機(jī)加載的類信息似枕、常量、靜態(tài)常量著隆、靜態(tài)方法等。線程共享呀癣。
  • 3.本地方法棧:為虛擬機(jī)執(zhí)行使用到的Native方法服務(wù)美浦,線程私有。
  • 4.虛擬機(jī)棧:虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:方法被調(diào)用時(shí)創(chuàng)建棧幀 ------>局部變量表 -------->局部變量项栏、對(duì)象引用浦辨。線程私有。
  • 5.程序計(jì)數(shù)器:線程切換后能恢復(fù)到正確的執(zhí)行位置沼沈。線程私有流酬。
  • 6.常量池:方法區(qū)的一部分
名稱 特征 作用 配置參數(shù) 異常
程序計(jì)數(shù)器 占用內(nèi)存小,生命周期與線程相同 大致為字節(jié)碼行號(hào)指示器
虛擬機(jī)棧 生命周期與線程相同庆冕,使用連續(xù)內(nèi)存空間 存儲(chǔ)局部變量表康吵、操作棧動(dòng)態(tài)鏈接、方法出口等信息 -Xss 設(shè)置棧的大小 StackOverFlowError
OutOfMemoryError
生命周期與虛擬機(jī)相同访递,可以不使用連續(xù)的內(nèi)存地址 保存對(duì)象實(shí)例晦嵌,所有對(duì)象實(shí)例(包括數(shù)組)都要在堆上分配 -Xms: 最大堆,默認(rèn)為物理內(nèi)存的1/4
-Xsx:默認(rèn)為物理內(nèi)存的1/64
-Xmn:新生代大小
OutOfMemoryError
方法區(qū) 生命周期與虛擬機(jī)相同拷姿,可以使用不連續(xù)內(nèi)存 存儲(chǔ)類信息惭载、常量、靜態(tài)變量响巢、即時(shí)編譯器編譯后的代碼等數(shù)據(jù) -XX:PermSize //永久代最小大小
-XX:MaxPermSize //最大大小
OutOfMemoryError
運(yùn)行時(shí)常量池 方法區(qū)的一部分描滔,具有動(dòng)態(tài)性 存放字面量及符號(hào)引用
1.1運(yùn)行時(shí)數(shù)據(jù)區(qū)域存儲(chǔ)的內(nèi)容

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

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

1.什么時(shí)程序計(jì)數(shù)器
程序計(jì)數(shù)器(Program counter Register)是一塊較小的內(nèi)存空間,它可以看作時(shí)當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)的指示器踪古。
2.程序計(jì)數(shù)器的作用

  • 字節(jié)碼解釋器工作通過改變程序計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令含长。如分支、循環(huán)伏穆、跳轉(zhuǎn)拘泞、異常處理、線程恢復(fù)等基礎(chǔ)功能枕扫。
  • 多線程情況下陪腌,程序計(jì)數(shù)器表示當(dāng)前線程執(zhí)行的位置,從而在線程切換的時(shí)候知道此線程上一次執(zhí)行的位置在哪里。

3.程序計(jì)數(shù)器的特點(diǎn)

  • 一塊較小的內(nèi)存空間
  • 每個(gè)線程獨(dú)立的程序計(jì)數(shù)器
  • 生命周期隨著線程創(chuàng)建和死亡诗鸭。
  • 如果線程執(zhí)行的時(shí)Java方法染簇,程序計(jì)數(shù)器記錄的時(shí)當(dāng)前正在執(zhí)行的字節(jié)碼指令地址。如果執(zhí)行的時(shí)Native方法强岸,程序計(jì)數(shù)器的值則為空(Undefined)锻弓。
  • 此內(nèi)存區(qū)域時(shí)Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
  • 由于Java虛擬機(jī)多線程時(shí)通過線程輪流切換并分配處理器執(zhí)行時(shí)間方式來時(shí)間请唱,在任何一個(gè)確定的時(shí)刻弥咪。一個(gè)處理器(多核處理器來說是一個(gè)內(nèi)核)都只會(huì)執(zhí)行一條線程中的指令过蹂。
  • 為了線程切換后能恢復(fù)到正確的執(zhí)行位置十绑,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器。

1.3 虛擬機(jī)棧

1.什么是虛擬機(jī)棧
虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型酷勺。每個(gè)方法在執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表本橙,操作數(shù)棧,方法出口脆诉,動(dòng)態(tài)鏈接等信息甚亭,每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)這一個(gè)棧幀從虛擬機(jī)中入棧到出棧的過程击胜。

2.什么是局部變量表
存放了編譯期可知的各種基本類型(boolean,byte,char,short,int long,float,double)亏狰,對(duì)象引用(reference類型)和returnAddress類型(指向了一條字節(jié)碼指令地址),其中64位長(zhǎng)度long和double類型的數(shù)據(jù)會(huì)占用2個(gè)局部變量的空間偶摔,其余數(shù)據(jù)類型占1個(gè)暇唾。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí)辰斋,這個(gè)方法需要在棧幀分配多大的局部變量是完全確定的策州,在運(yùn)行期間棧幀不會(huì)改變局部變量表的大小空間。


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

3.Java虛擬機(jī)棧的特點(diǎn)

  • 1.每個(gè)線程私有宫仗,生命周期和線程相同
  • 2.每個(gè)方法在執(zhí)行的同時(shí)够挂,創(chuàng)建一個(gè)棧幀。
  • 3.局部變量表所需的內(nèi)存空間在編譯期間完成分配藕夫,在運(yùn)行期間不會(huì)改變局部變量表大小孽糖。
    1. 在Java虛擬機(jī)規(guī)范中,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常:
      StackOverFlowError:如果線程請(qǐng)求的棧深度太深毅贮,超出Xss規(guī)定棧的最大空間办悟,超出就會(huì)報(bào)錯(cuò)。
      OutOfMemoryError:虛擬機(jī)椖勐耄可以動(dòng)態(tài)擴(kuò)展誉尖,如果擴(kuò)展到無法申請(qǐng)足夠內(nèi)存空間。
      Xss:每個(gè)線程堆棧大小 默認(rèn)1M铸题。
1.4 本地方法棧

1.本地方法棧與虛擬機(jī)棧發(fā)揮的作用非常相似铡恕,他們之間的區(qū)別:虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法服務(wù)琢感,而本地方法棧則為虛擬機(jī)執(zhí)行Native服務(wù)。
2.有的虛擬機(jī)直接就把本地方法棧和虛擬機(jī)棧合二為一 (比如Sun HotSpot虛擬機(jī))
3.也會(huì)有StackOverFlowError和OutOfMemoryError異常探熔。


1.5 堆

堆是JVM中內(nèi)存占用最大驹针,管理最復(fù)雜的一個(gè)區(qū)域,其唯一用途就是存放對(duì)象實(shí)例诀艰。所有的對(duì)象實(shí)例及數(shù)組都在堆上進(jìn)行分配柬甥。1.7后,字符串常量池從永久代中剝離出來其垄,放在堆中苛蒲。

JDK1.7堆內(nèi)存結(jié)構(gòu)

并不是所有對(duì)象都在堆上,由于棧上分配和標(biāo)量替換绿满,導(dǎo)致有些對(duì)象不在堆上臂外。

堆大小通過 -Xms(最小值)和-Xmx(最大值)參數(shù)設(shè)置。

  • -Xms為JVM啟動(dòng)時(shí)申請(qǐng)的最小內(nèi)存喇颁,默認(rèn)為操作系統(tǒng)物理內(nèi)存的1/64 但小于1G漏健。
  • -Xmx為JVM可申請(qǐng)的最大內(nèi)存,默認(rèn)為物理內(nèi)存的1/4 但小于1G
  • 當(dāng)空余堆內(nèi)存小于40%時(shí)橘霎,JVM會(huì)增大Heap到-Xmx指定的大小蔫浆,可通過-XX:MinHeapFreeRation來指定這個(gè)比例。
  • 當(dāng)空余堆內(nèi)存大于70%時(shí)姐叁,JVM會(huì)減小heap大小到-Xms指定的大小瓦盛,可通過-XX:MaxHeapFreeRation 來指定這個(gè)比例。
  • 對(duì)于后端系統(tǒng)七蜘,為了避免在運(yùn)行時(shí)頻繁調(diào)整Heap大小谭溉,通常-Xms和-Xmx設(shè)置成一樣的值。

由于收集器都是采用分代收集算法橡卤,堆被劃分為新生代和老年代扮念。

新生代:主要存儲(chǔ)新創(chuàng)建和尚未進(jìn)入老年代
老年代:存儲(chǔ)經(jīng)過多次minor GC后仍然存活的對(duì)象。

堆中沒有足夠的內(nèi)存完成實(shí)例分配碧库,并且堆也無法擴(kuò)展時(shí)柜与,將會(huì)出現(xiàn)OOM異常(內(nèi)存溢出,內(nèi)存泄漏)滿足下面兩個(gè)條件就會(huì)OOM嵌灰。
1.JVM 98%的時(shí)間都花在內(nèi)存回收弄匕。
2.每次回收的內(nèi)存小于2%。

為什么要分代沽瞭?
給堆內(nèi)存分代是為了提高對(duì)象內(nèi)存分配和垃圾回收的效率迁匠。
如果沒有區(qū)域劃分,生命周期很長(zhǎng)的和新創(chuàng)建的對(duì)象放在一起,堆內(nèi)存需要頻繁進(jìn)行回收城丧,每次回收都遍歷所有對(duì)象延曙,花費(fèi)的時(shí)間代價(jià)是巨大的,嚴(yán)重影響GC效率亡哄。

內(nèi)存分代后枝缔,情況就不同了。

1.新的對(duì)象會(huì)在新生代中分配蚊惯。
2.經(jīng)過多次回收仍然存活的放在老年代中愿卸,靜態(tài)屬性和類信息等存放在永久代中。
3.新生代中的對(duì)象存活時(shí)間短截型,只需要在新生代中頻繁進(jìn)行GC
4.老年代中對(duì)象生命周期長(zhǎng)趴荸,內(nèi)存回收的頻率相對(duì)較低,不需要頻繁進(jìn)行回收菠劝。
5.永久代中回收效果差赊舶,一般不進(jìn)行回收睁搭。

新生代
程序創(chuàng)建的對(duì)象都是從新生代分配內(nèi)存赶诊。
新生代由Eden Space和兩塊相同大小的Survivor Space構(gòu)成,默認(rèn)比例為8:1:1
劃分的目的是因?yàn)镠otSpot采用復(fù)制算法來回收新生代园骆,設(shè)置這個(gè)比例是充分利用內(nèi)存空間舔痪,減少浪費(fèi)。
新生成的對(duì)象在Eden區(qū)分配(大對(duì)象除外锌唾,大對(duì)象直接進(jìn)入老年代)锄码,當(dāng)Eden區(qū)沒有足夠的空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次minor GC晌涕。

  • 1.GC開始時(shí)滋捶,對(duì)象只會(huì)存在于Eden區(qū)和From Survivor區(qū),To Survivor區(qū)是空的(作為保留區(qū)域)
  • 2.GC進(jìn)行時(shí)余黎,Eden區(qū)中所有存活的對(duì)象都會(huì)被復(fù)制到To Survivor區(qū)重窟。
  • 3.而在From Survivor區(qū)中,仍存活的對(duì)象會(huì)根據(jù)他們的年齡值決定去向惧财,年齡值達(dá)到年齡閾值的對(duì)象移到老年代巡扇,沒有達(dá)到的閾值會(huì)被復(fù)制到To Survivor區(qū)。
  • 4.接著清空Eden區(qū)和From Survivor區(qū)垮衷。
  • 5.新生代存活的對(duì)象都在To Survivor區(qū)厅翔。
  • 6.接著,F(xiàn)rom Survivor區(qū)和To Survivor區(qū)會(huì)交換他們的角色搀突,也就是新的To Survivor區(qū)就是上次GC清空的From Survivor區(qū)刀闷,新的From Survivor區(qū)就是上次GC的To Survivor區(qū),總之,不管怎樣都會(huì)保證To Survivor區(qū)在議論GC后是空的甸昏。
  • 7.GC時(shí)戈次,To Survivor區(qū)沒有足夠的空間存放上一次新生代收集下來的存活對(duì)象時(shí),需要依賴?yán)夏甏M(jìn)行分配擔(dān)保筒扒,將這些對(duì)象存放在老年代中怯邪。

-Xmn 來指定新生代大小
也可以通過 -XX:SurvivorRation來調(diào)整Eden Space及Survivor Space的大小。

老年代
用于存放經(jīng)過多次新生代GC依然存活的對(duì)象花墩,老年代中的對(duì)象生命周期較長(zhǎng)悬秉,存活率比較高,在老年代中進(jìn)行GC的頻率相對(duì)而言較低冰蘑,而且回收的速度也比較慢和泌。
老年代所占的內(nèi)存大小為-Xmx 減去 -Xmn
主要存儲(chǔ)的有:緩存對(duì)象,新建的對(duì)象也有可能直接進(jìn)入老年代祠肥。
主要有兩種情況武氓。

  1. 大對(duì)象,可通過啟動(dòng)參數(shù)設(shè)置:-XX:PretenureSizeThreshold=1024(單位字節(jié)仇箱,默認(rèn)為0)來代表超過多大時(shí)就不在新生代分配县恕,而是直接在老年代分配。
    2.大的數(shù)組對(duì)象剂桥,且數(shù)組中無引用外部對(duì)象忠烛。

堆的特點(diǎn):
1.Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建权逗。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例和數(shù)組美尸,幾乎所有的對(duì)象實(shí)例都在這里分配。
2.Java堆是垃圾收集器管理的主要區(qū)域斟薇。
3.Java堆還可以細(xì)分為:新生代和老年代师坎;在細(xì)分一點(diǎn)有Eden空間、From Survivor空間堪滨、To Survivor空間等胯陋。
4.可以位于物理上不連續(xù)的空間,但是邏輯上要連續(xù)椿猎。
5.OutOfMemoryError:如果堆中沒有內(nèi)存完成實(shí)例分配惶岭,并且堆也無法再擴(kuò)展時(shí),拋出該異常犯眠。
6.新生代和老年代比例默認(rèn)1:2 新生代Eden和From按灶、To三個(gè)區(qū)域,比例默認(rèn)為8:1:1

  1. -Xms 最小堆大小 默認(rèn)為物理內(nèi)存的1/64 但小于1G -Xmx 最大堆大小 默認(rèn)為物理內(nèi)存的1/4 但小于1G -Xmn新生代大小筐咧。
堆區(qū)域劃分

默認(rèn)比例

方法區(qū)是JVM的規(guī)范鸯旁,而永久代是方法區(qū)的一種實(shí)現(xiàn)噪矛,并且只有HotSpot才有永久代,而對(duì)于其他類型的虛擬機(jī)并沒有铺罢。在JDK1.8中艇挨,HotSpot已經(jīng)沒有永久代這個(gè)區(qū)間了,取而代之的是MetaSpace韭赘。

1.6 方法區(qū)

屬于共享內(nèi)存區(qū)域缩滨,存儲(chǔ)已被虛擬機(jī)加載的類信息,常量泉瞻、靜態(tài)變量脉漏、即時(shí)編譯后的代碼等數(shù)據(jù)。
默認(rèn)最小值為16MB袖牙,最大值為64MB侧巨,可以通過-XX:PermSize 和-XX:MaxPermSize 參數(shù)限制方法區(qū)的大小
運(yùn)行時(shí)常量池:是方法區(qū)的一部分。Class文件中除了有類的版本鞭达、字段司忱、方法、接口等描述信息外畴蹭,還有一項(xiàng)信息是常量池坦仍,用于存放編譯器生成的各種符號(hào)引用,這部分內(nèi)容將在類加載后方法方法區(qū)的運(yùn)行時(shí)常量池中撮胧。
方法區(qū)空間不夠的時(shí)候出現(xiàn)OOM桨踪。(主流框架中,通過字節(jié)碼技術(shù)動(dòng)態(tài)生成大量的Class)芹啥。


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

屬于方法區(qū)的一部分,用于存放編譯期生成的各種字面量和符號(hào)引用铺峭。編譯器和運(yùn)行期(String的intern())都可以將常量放入池中墓怀。
字面量:比較借金額Java語言的常量概念,如文本字符串卫键,被聲明為final的常量值等傀履。
符號(hào)引用:屬于編譯原理方面的概念,包括以下三類常量:類和接口的全限定名莉炉、字段的名稱和描述符钓账、方法的名稱和描述符。
因?yàn)檫\(yùn)行時(shí)常量池是方法區(qū)的一部分絮宁,那么當(dāng)常量池?zé)o法再申請(qǐng)到內(nèi)存時(shí)也會(huì)拋出OOM異常梆暮。


1.8 直接內(nèi)存

什么是直接內(nèi)存?
非JVM內(nèi)存绍昂,操作系統(tǒng)的部分內(nèi)存啦粹。
JDK1.4加入的NIO類偿荷,引入了一種基于通道(Channel)和緩存(Buffer)的I/O方式,它可以使用Native函數(shù)庫(kù)直接分配堆外內(nèi)存唠椭,然后通過一個(gè)存儲(chǔ)在Java堆中的DirectByteBuffer對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作跳纳。可以避免在Java堆和Native堆中來回的數(shù)據(jù)耗時(shí)操作贪嫂。
OutOfMemoryError:會(huì)受到本機(jī)內(nèi)存限制寺庄,如果內(nèi)存區(qū)域總和大于物理內(nèi)存從而導(dǎo)致動(dòng)態(tài)擴(kuò)展時(shí)出現(xiàn)該異常。
NIO的Buffer提供了一個(gè)可以不經(jīng)過JVM內(nèi)存直接訪問系統(tǒng)物理的內(nèi)存類--DirectByteBuffer力崇。DirectByteBuffer類繼承自ByteBuffer铣揉,但和普通的ByteBuffer不同,普通的ByteBuffer扔在JVM堆上分配內(nèi)存餐曹,其最大的內(nèi)存收到最大堆限制逛拱,而DirectByteByffer直接分配在物理內(nèi)存中,并不占用堆空間台猴,其可申請(qǐng)的最大內(nèi)存受操作系統(tǒng)限制朽合。

區(qū)別和應(yīng)用場(chǎng)景
1.直接內(nèi)存的讀寫操作比普通buffer快,但它的創(chuàng)建饱狂、銷毀比普通的Buffer慢曹步。
2.因此直接內(nèi)存使用于需要大內(nèi)存空間且頻繁訪問的場(chǎng)合,不適合用于頻繁申請(qǐng)釋放內(nèi)存的場(chǎng)合休讳。

2.JDK1.8內(nèi)存模型- 運(yùn)行時(shí)數(shù)據(jù)區(qū)域


2.1 JDK1.8 內(nèi)存模型

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

2.2 JDK1.8 VS JDK1.7
  1. 1.8同1.7比讲婚,最大的差別就是:元空間取代了永久代。
  2. 元空間的本質(zhì)和永久代類似俊柔,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)筹麸,所以原來屬于方法區(qū)的運(yùn)行常量池就屬于元空間了。
  3. 元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中雏婶,而是使用本地內(nèi)存物赶。
  4. 在JDK1.7之前運(yùn)行時(shí)常量池邏輯包含字符串常量池存放在方法區(qū),此時(shí)hotspot虛擬機(jī)對(duì)方法區(qū)實(shí)現(xiàn)為永久代(運(yùn)行時(shí)常量池和字符串常量池都在JVM)中
  5. 在JDK1.7中 字符串常量池被從方法區(qū)拿到了堆中留晚,這里沒有提到運(yùn)行時(shí)常量池酵紫,也就是說字符串常量池被單獨(dú)拿到堆中,運(yùn)行時(shí)常量池剩下的東西還在方法區(qū)错维,也就是hotspot中的永久代(運(yùn)行時(shí)常量池在永久代奖地,字符串常量池在堆中)
  6. 在JDK1.8 hotspot移除了永久代取而代之的是元空間,這時(shí)字符串常量池還在堆中赋焕,運(yùn)行時(shí)常量池在元空間参歹。元空間已經(jīng)挪到JVM外。(運(yùn)行時(shí)常量池在元空間宏邮,字符串常量池在堆中)

2.3 堆

JDK1.8中把存放元數(shù)據(jù)的永久代從堆內(nèi)存移動(dòng)到了本地內(nèi)存中泽示,JDK1.8內(nèi)存結(jié)構(gòu)就變成了如下:


1.8堆結(jié)構(gòu)

實(shí)際上在JDK1.7中缸血,存儲(chǔ)在永久代的部分?jǐn)?shù)據(jù)就已經(jīng)轉(zhuǎn)移到了Java Heap或者是Native Heap。
但永久代仍存在于JDK1.7中械筛,并沒有完全移除捎泻。譬如:
符號(hào)引用(Symbols)轉(zhuǎn)移到了native heap。
字面量(interned strings)轉(zhuǎn)移到了java heap埋哟。
類的靜態(tài)變量(class statics)轉(zhuǎn)移到了java heap笆豁。

元空間本質(zhì)和永久代類似,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)赤赊。
不過元空間和永久代最大區(qū)別是 元空間在本地內(nèi)存闯狱,永久代在JVM中。
元空間僅受本地內(nèi)存限制抛计,但可以同過-XX:metaSpaceSize調(diào)整哄孤。

取消永久代的原因:
1.字符串存在永久代中,容易出現(xiàn)性能問題和內(nèi)存溢出吹截。
2.類及方法的信息等比較難確定大小瘦陈,因此對(duì)永久代大小指定比較困難。太小容易永久代溢出波俄,太大容易老年代溢出晨逝。
3.永久代會(huì)為GC帶來不必要的復(fù)雜度,并且回收效率低懦铺。


2.4 元空間

JDK8的HotSpot JVM現(xiàn)在使用的是本地內(nèi)存來表示類的元數(shù)據(jù)捉貌,這個(gè)區(qū)域就叫做元空間。

1.元空間的本質(zhì)和永久代類似冬念,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)趁窃。
2.不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存刘急。
3.在JDK1.8中棚菊,永久代已經(jīng)不存在,存儲(chǔ)的類信息叔汁,編譯后代碼等數(shù)據(jù)已經(jīng)移動(dòng)到了MetaSpace中。
4.原來靜態(tài)變量(class statics)和字面量(Interned Strings)都被轉(zhuǎn)移到了Java堆中检碗。(JDK1.7開始)
5.永久代刪除后据块,JVM胡烈PermSize和MaxPermSize這兩個(gè)參數(shù),再也看不到java.lang.OutOfMemoryError:PermGen error的異常了折剃。
6.元空間大小僅受本地內(nèi)存限制另假,可以通過以下參數(shù)來指定元空間大小

  • -XX:MetaspaceSize,初始空間大小怕犁,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載边篮,同時(shí)GC會(huì)對(duì)該值進(jìn)行調(diào)整:如果釋放了大量的空間己莺,就適當(dāng)降低該值;如果釋放了很少的空間戈轿,那么在不超過MaxMetaspaceSize時(shí)凌受,適當(dāng)提高該值
  • -XX:MaxMetaspaceSize,最大空間思杯,默認(rèn)是沒有限制的
  • -XX:MinMetaspaceFreeRatio胜蛉,在GC之后,最小的Metaspace剩余空間容量的百分比色乾,減少為分配空間所導(dǎo)致的垃圾收集
  • -XX:MaxMetaspaceFreeRatio誊册,在GC之后,最大的Metaspace剩余空間容量的百分比暖璧,減少為釋放空間所導(dǎo)致的垃圾收集

2.5 方法區(qū)

所以對(duì)于方法區(qū)案怯,Java8之后的變化:
1.移除永久代(PermGen),替換為元空間(MetaSpace)
2.永久代中的class metadata轉(zhuǎn)移到本地內(nèi)存(本地內(nèi)存澎办,而不是虛擬機(jī))
3.永久代中的字面量(interned String)和類靜態(tài)變量(class static variables)轉(zhuǎn)移到了Java Heap.


3.OOM && SOF

OutOfMemoryError異常:除了程序計(jì)數(shù)器外嘲碱,虛擬機(jī)內(nèi)存的其他幾個(gè)運(yùn)行時(shí)區(qū)域都有發(fā)生OutOfMemory異常的可能。
內(nèi)存泄漏:指程序中動(dòng)態(tài)分配內(nèi)存給一下臨時(shí)對(duì)象浮驳,但是對(duì)象不會(huì)被GC所回收悍汛,它始終占用內(nèi)存。即被分配的對(duì)象可達(dá)但是已無用至会。
內(nèi)存溢出:指程序運(yùn)行過程中無法申請(qǐng)到足夠的內(nèi)存而導(dǎo)致的离咐。內(nèi)存不夠時(shí),會(huì)先進(jìn)行垃圾回收奉件,回收后仍然空間不夠宵蛀。內(nèi)存泄漏是內(nèi)存溢出的一種誘因,不是唯一因素县貌。

3.1 發(fā)生內(nèi)存泄漏或溢出了怎么辦

一般的異常信息:java.lang.OutOfMemoryError:Java heap spacess

Java堆用于存儲(chǔ)對(duì)象實(shí)例术陶,我們只要不斷的創(chuàng)建對(duì)象,并且保證GC Roots到對(duì)象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對(duì)象煤痕,就會(huì)在對(duì)象數(shù)量達(dá)到最大堆容量限制后產(chǎn)生內(nèi)存溢出梧宫。
1.-XX:+HeapDumpOnOutOfMemoryError 讓虛擬機(jī)在出現(xiàn)OOM時(shí)候Dump出內(nèi)存映像便于分析。
2.使用內(nèi)存映像分析工具(Jstack摆碉、Jmap塘匣、Jconsole)對(duì)映像進(jìn)行分析。確定出是因?yàn)閮?nèi)存泄漏還是內(nèi)存溢出導(dǎo)致的巷帝。
3.如果是內(nèi)存泄漏忌卤,進(jìn)一步通過工具查看泄漏對(duì)象GC Roots的引用鏈。于是就能找到引用信息楞泼,定位內(nèi)存泄漏代碼位置驰徊。
4.如果不是內(nèi)存泄漏笤闯,就檢查虛擬機(jī)參數(shù)(-Xmx和-Xms)設(shè)置的是否合理。并修改代碼棍厂,把某些對(duì)象生命周期過長(zhǎng)颗味,持有狀態(tài)時(shí)間過長(zhǎng)的代碼修改。


3.2 Java Heap溢出

在JVM規(guī)范中勋桶,堆中的內(nèi)存是用來生成對(duì)象實(shí)例和數(shù)組的脱衙。
如果細(xì)分、堆內(nèi)存還可以分為年輕代和年老代例驹、年輕代包括一個(gè)Eden和兩個(gè)survivor區(qū)捐韩。
當(dāng)生成新對(duì)象時(shí),內(nèi)存申請(qǐng)過程如下:
1.JVM先嘗試在eden區(qū)分配新建對(duì)象所需的內(nèi)存鹃锈。
2.如果內(nèi)存大小足夠荤胁,申請(qǐng)結(jié)束,否則啟動(dòng)young GC屎债。
3.JVM啟動(dòng)youngGC,試圖將eden區(qū)中不活躍的對(duì)象釋放掉仅政,釋放后若Eden空間仍然不足以放入新對(duì)象陵刹,則試圖將部分Eden中活躍對(duì)象放入Sruvivor區(qū)蒂誉。
4.Survivor區(qū)被用來作為Eden及old區(qū)中間交換區(qū)域,當(dāng)old區(qū)空間足夠時(shí)虫给,Survivor區(qū)的對(duì)象會(huì)被移到old區(qū)躯喇。否則會(huì)被保留在Survivor區(qū)辫封。
5.當(dāng)old區(qū)空間不夠時(shí),JVM會(huì)在old區(qū)進(jìn)行full GC廉丽。
6.full GC后倦微,若Survivor及old去仍然無法存放從Eden復(fù)制過來的部分對(duì)象,導(dǎo)致JVM無法在Eden區(qū)為新的對(duì)象創(chuàng)建內(nèi)存區(qū)域正压,則出現(xiàn)OOM欣福。


4.3 虛擬機(jī)棧和本地方法棧溢出

1.如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的最大深度,將拋出StackOverflow異常焦履。
2.不斷創(chuàng)建線程拓劝,如果虛擬機(jī)在擴(kuò)展棧時(shí)無法申請(qǐng)到足夠的內(nèi)存空間,則拋出OutOfMemoryError異常嘉裤。
3.這里需要注意:當(dāng)Xss設(shè)置的大小越大凿将,可分配的線程數(shù)就越少。


4.4 運(yùn)行時(shí)常量池溢出(針對(duì)JDK1.7以前)

異常信息:java.lang.OutOfMemoryError:PermGen space
如果要向運(yùn)行時(shí)常量池中添加內(nèi)容,最簡(jiǎn)單的做法就是使用String.intern()這個(gè)Native方法价脾。如果池中已經(jīng)包含一個(gè)等于此String的字符串,則返回常量池中的這個(gè)字符串笛匙,否則侨把,將此字符串添加到常量池中犀变,并且返回此字符串的引用。


4.5 方法區(qū)溢出(針對(duì)JDK1.7及以前)

異常信息:java.lang.OutOfMemoryError:PermGen space
方法區(qū)用于存放Class的相關(guān)信息秋柄,如類名获枝,訪問修飾符,常量池骇笔,字段描述省店,方法描述。
所以如果程序加載類過多笨触,或者使用反射懦傍、cglib代理等動(dòng)態(tài)代理生成類的技術(shù),就可能導(dǎo)致該區(qū)發(fā)生內(nèi)存溢出芦劣。


4.6 OutOfMemoryError:GC overhead limit exceeded

原因:執(zhí)行垃圾回收的時(shí)間比例太大粗俱,有效運(yùn)算量太小。默認(rèn)情況下如果垃圾回收超過98%虚吟,并且GC回收的內(nèi)存小于2%寸认,JVM就會(huì)拋這個(gè)異常。
一般是應(yīng)用程序在有限的內(nèi)存上創(chuàng)建了大量的臨時(shí)對(duì)象或者弱引用對(duì)象串慰,從而導(dǎo)致該異常偏塞。
解決方法:
1.大對(duì)象在使用后指向null
2.增加參數(shù) -XX:-UseGCOverheadLimit 關(guān)閉這個(gè)特性。
3.增加堆大小邦鲫,-Xmx


4.7 StackOverflow 堆棧溢出

StackOverFlowError:當(dāng)應(yīng)用程序遞歸太深而發(fā)生堆棧溢出時(shí)灸叼,拋出該錯(cuò)誤。棧一般默認(rèn)為1M掂碱,一旦出現(xiàn)死循環(huán)或者大量的遞歸調(diào)用怜姿,在不斷壓棧的過程中,造成棧容量超過1M而導(dǎo)致溢出疼燥。
棧溢出原因:
1.遞歸調(diào)用
2.大量循環(huán)或者死循環(huán)
3.全局變量是否過多
4.數(shù)組沧卢,List、map數(shù)據(jù)過大


4.8 內(nèi)存泄漏場(chǎng)景
  • 1.使用靜態(tài)的集合類:靜態(tài)集合類生命周期和應(yīng)用程序一樣長(zhǎng)醉者,所以JVM結(jié)束前都不會(huì)釋放但狭。造成內(nèi)存泄漏
  • 2.單例模式可能會(huì)造成內(nèi)存泄漏(長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用):?jiǎn)卫芷诤蛻?yīng)用程序羊場(chǎng),如果單例中擁有另一個(gè) 對(duì)象的引用撬即。那么就會(huì)內(nèi)存泄漏立磁。
  • 3.數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)剥槐、輸入輸出這些資源沒有顯式的關(guān)閉:如果對(duì)象正在使用資源唱歧,比如輸入輸出,Java無法判斷這些對(duì)象是不是正在進(jìn)行操作,所以不能回收颅崩。資源使用完后要調(diào)用close()方法關(guān)閉几于。
  • 4.存在于集合中的對(duì)象,被修改了hashCode沿后,會(huì)造成內(nèi)存泄漏沿彭。
4.9 如何避免發(fā)生內(nèi)存泄漏和溢出

1.盡早釋放無用對(duì)象的引用。
2.使用StringBuffer或Build尖滚,避免使用String
3.盡量少用靜態(tài)集合類喉刘。
4.避免在循環(huán)中創(chuàng)建大量對(duì)象。
5.開啟大文件或從數(shù)據(jù)庫(kù)一次拿太多數(shù)據(jù)容易造成溢出漆弄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末睦裳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子置逻,更是在濱河造成了極大的恐慌推沸,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件券坞,死亡現(xiàn)場(chǎng)離奇詭異鬓催,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恨锚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門宇驾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猴伶,你說我怎么就攤上這事课舍。” “怎么了他挎?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵筝尾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我办桨,道長(zhǎng)筹淫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任呢撞,我火速辦了婚禮损姜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘殊霞。我一直安慰自己摧阅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布绷蹲。 她就那樣靜靜地躺著棒卷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娇跟,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天岩齿,我揣著相機(jī)與錄音,去河邊找鬼苞俘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛龄章,可吹牛的內(nèi)容都是我干的吃谣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼做裙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼岗憋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锚贱,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤仔戈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拧廊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體监徘,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年吧碾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凰盔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倦春,死狀恐怖户敬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睁本,我是刑警寧澤尿庐,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站呢堰,受9級(jí)特大地震影響抄瑟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暮胧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一锐借、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧往衷,春花似錦钞翔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春汰扭,著一層夾襖步出監(jiān)牢的瞬間稠肘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工萝毛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留项阴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓笆包,卻偏偏與公主長(zhǎng)得像环揽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庵佣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容