網(wǎng)站建設(shè)修改建議個人網(wǎng)站的制作模板
堆(Heap)的核心概述
堆針對一個JVM進程來說是唯一的,也就是一個進程只有一個JVM,但是進程包含多個線程,他們是共享同一堆空間的。
一個JVM實例只存在一個堆內(nèi)存,堆也是Java內(nèi)存管理的核心區(qū)域。
Java堆區(qū)在JVM啟動的時候即被創(chuàng)建,其空間大小也就確定了。是JVM管理的最大一塊內(nèi)存空間。
- 堆內(nèi)存的大小是可以調(diào)節(jié)的。
《Java虛擬機規(guī)范》規(guī)定,堆可以處于物理上不連續(xù)的內(nèi)存空間中,但在邏輯上它應該被視為連續(xù)的。
所有的線程共享Java堆,在這里還可以劃分線程私有的緩沖區(qū)(Thread Local Allocation Buffer,TLAB)。
《Java虛擬機規(guī)范》中對Java堆的描述是:所有的對象實例以及數(shù)組都應當在運行時分配在堆上。(
The heap is the run-time data area from which memory for all class instances and arrays is allocated
)
數(shù)組和對象可能永遠不會存儲在棧上,因為棧幀中保存引用,這個引用指向?qū)ο蠡蛘邤?shù)組在堆中的位置。
在方法結(jié)束后,堆中的對象不會馬上被移除,僅僅在垃圾收集的時候才會被移除。
堆,是GC(Garbage Collection,垃圾收集器)執(zhí)行垃圾回收的重點區(qū)域。
堆內(nèi)存細分?
Java 7及之前堆內(nèi)存邏輯上分為三部分:新生區(qū)+養(yǎng)老區(qū)+永久區(qū)
- Young Generation Space 新生區(qū) Young/New 又被劃分為Eden區(qū)和Survivor區(qū)
- Tenure generation space 養(yǎng)老區(qū) Old/Tenure
- Permanent Space 永久區(qū) Perm
Java 8及之后堆內(nèi)存邏輯上分為三部分:新生區(qū)+養(yǎng)老區(qū)+元空間
- Young Generation Space 新生區(qū) Young/New 又被劃分為Eden區(qū)和Survivor區(qū)
- Tenure generation space 養(yǎng)老區(qū) Old/Tenure
- Meta Space 元空間 Meta
約定:新生區(qū)(代)<=>年輕代 、 養(yǎng)老區(qū)<=>老年區(qū)(代)、 永久區(qū)<=>永久代
堆空間內(nèi)部結(jié)構(gòu)(JDK7)
堆空間內(nèi)部結(jié)構(gòu)(JDK8)?
設(shè)置堆內(nèi)存大小與OOM?
堆空間大小的設(shè)置
Java堆區(qū)用于存儲Java對象實例,那么堆的大小在JVM啟動時就已經(jīng)設(shè)定好了,可以通過選項"-Xmx"和"-Xms"來進行設(shè)置。
- “-Xms"用于表示堆區(qū)的起始內(nèi)存,等價于
-XX:InitialHeapSize
- “-Xmx"則用于表示堆區(qū)的最大內(nèi)存,等價于
-XX:MaxHeapSize
一旦堆區(qū)中的內(nèi)存大小超過“-Xmx"所指定的最大內(nèi)存時,將會拋出OutOfMemoryError異常。
通常會將-Xms和-Xmx兩個參數(shù)配置相同的值,其目的是為了能夠在ava垃圾回收機制清理完堆區(qū)后不需要重新分隔計算堆區(qū)的大小,從而提高性能。
默認情況下
- 初始內(nèi)存大小:物理電腦內(nèi)存大小 / 64
- 最大內(nèi)存大小:物理電腦內(nèi)存大小 / 4
OutOfMemory舉例?
public class OOMTest {public static void main(String[]args){ArrayList<Picture> list = new ArrayList<>();while(true){try {Thread.sleep(20);} catch (InterruptedException e){e.printStackTrace();}list.add(new Picture(new Random().nextInt(1024*1024)));}}
}
Exception in thread "main" java.lang.OutofMemoryError: Java heap space
? ? at com.atguigu. java.Picture.<init>(OOMTest. java:25)
? ? at com.atguigu.java.O0MTest.main(OOMTest.java:16)?
年輕代與老年代?
存儲在JVM中的Java對象可以被劃分為兩類:
- 一類是生命周期較短的瞬時對象,這類對象的創(chuàng)建和消亡都非常迅速
- 另外一類對象的生命周期卻非常長,在某些極端的情況下還能夠與JVM的生命周期保持一致
Java堆區(qū)進一步細分的話,可以劃分為年輕代(YoungGen)和老年代(oldGen)
其中年輕代又可以劃分為Eden空間、Survivor0空間和Survivor1空間(有時也叫做from區(qū)、to區(qū))
?
下面這參數(shù)開發(fā)中一般不會調(diào):?
配置新生代與老年代在堆結(jié)構(gòu)的占比。
- 默認
-XX:NewRatio=2
,表示新生代占1,老年代占2,新生代占整個堆的1/3
- 可以修改
-XX:NewRatio=4
,表示新生代占1,老年代占4,新生代占整個堆的1/5
在HotSpot中,Eden空間和另外兩個survivor空間缺省所占的比例是8:1:1
當然開發(fā)人員可以通過選項“-xx:SurvivorRatio
”調(diào)整這個空間比例。比如-xx:SurvivorRatio=8
幾乎所有的Java對象都是在Eden區(qū)被new出來的。絕大部分的Java對象的銷毀都在新生代進行了。
- IBM公司的專門研究表明,新生代中80%的對象都是“朝生夕死”的。
可以使用選項"-Xmn
"設(shè)置新生代最大內(nèi)存大小,這個參數(shù)一般使用默認值就可以了
?
圖解對象分配過程?
?為新對象分配內(nèi)存是一件非常嚴謹和復雜的任務,JVM的設(shè)計者們不僅需要考慮內(nèi)存如何分配、在哪里分配等問題,并且由于內(nèi)存分配算法與內(nèi)存回收算法密切相關(guān),所以還需要考慮GC執(zhí)行完內(nèi)存回收后是否會在內(nèi)存空間中產(chǎn)生內(nèi)存碎片。
A.new的對象先放伊甸園區(qū)。此區(qū)有大小限制。
B.當伊甸園的空間填滿時,程序又需要創(chuàng)建對象,JVM的垃圾回收器將對伊甸園區(qū)進行垃圾回收(MinorGC),將伊甸園區(qū)中的不再被其他對象所引用的對象進行銷毀。再加載新的對象放到伊甸園區(qū)
C.然后將伊甸園中的剩余對象移動到幸存者0區(qū)。
D.如果再次觸發(fā)垃圾回收,此時上次幸存下來的放到幸存者0區(qū)的,如果沒有回收,就會放到幸存者1區(qū)。
F.如果再次經(jīng)歷垃圾回收,此時會重新放回幸存者0區(qū),接著再去幸存者1區(qū)。
G.什么時候能去養(yǎng)老區(qū)呢?可以設(shè)置次數(shù)。默認是15次。
-
- 可以設(shè)置參數(shù):進行設(shè)置
-Xx:MaxTenuringThreshold= N
- 可以設(shè)置參數(shù):進行設(shè)置
H.在養(yǎng)老區(qū),相對悠閑。當養(yǎng)老區(qū)內(nèi)存不足時,再次觸發(fā)GC:Major GC,進行養(yǎng)老區(qū)的內(nèi)存清理
若養(yǎng)老區(qū)執(zhí)行了Major GC之后,發(fā)現(xiàn)依然無法進行對象的保存,就會產(chǎn)生OOM異常。
java.lang.OutofMemoryError: Java heap space
- 針對幸存者s0,s1區(qū)的總結(jié):復制之后有交換,誰空誰是to,交換以后標記++。到15以后跑到老年區(qū)
- 關(guān)于垃圾回收:頻繁在新生區(qū)收集,很少在老年代收集,幾乎不再永久代和元空間進行收集