JVM 堆內(nèi)存
1. 前言
本節(jié)主要講解運(yùn)行時數(shù)據(jù)區(qū)的堆內(nèi)存。本節(jié)主要知識點(diǎn)如下:
- 掌握堆內(nèi)存空間結(jié)構(gòu)圖,從總體層面認(rèn)識堆內(nèi)存,為本節(jié)重點(diǎn)內(nèi)容之一;
- 了解 JVM 堆空間的基本概念,為本節(jié)的基礎(chǔ)知識點(diǎn);
- 了解堆內(nèi)存的分代概念,年輕代,eden區(qū),from/to,幸存者及老年代,為本節(jié)核心知識點(diǎn),后續(xù)對垃圾回收講解時,大部分的回收都是發(fā)生在堆內(nèi)存中,掌握分代概念是學(xué)習(xí)垃圾回收機(jī)制的必要前提。
2. 堆內(nèi)存結(jié)構(gòu)
堆內(nèi)存是運(yùn)行時數(shù)據(jù)區(qū)中非常重要的結(jié)構(gòu),實(shí)例對象會存放于堆內(nèi)存中。在后續(xù)小節(jié)中,我們講解 GC 垃圾回收器,絕大多數(shù)的垃圾回收都發(fā)生在堆內(nèi)存中,因此對于 JVM 來說,堆內(nèi)存占據(jù)著十分重要的且不可替代的位置。
我們先來看下堆內(nèi)存的結(jié)構(gòu)圖,初步了解堆內(nèi)存的整體內(nèi)存劃分。
從上圖可以看到如下幾個要點(diǎn):
- 堆內(nèi)存從結(jié)構(gòu)上來說分為年輕代(YoungGen)和老年代(OldGen)兩部分;
- 年輕代(YoungGen)又可以分為生成區(qū)(Eden)和幸存者區(qū)(Survivor)兩部分;
- 幸存者區(qū)(Survivor)又可細(xì)分為 S0區(qū)(from space)和 S1區(qū) (to space)兩部分。
從圖中,我們能夠大體了解堆內(nèi)存的結(jié)構(gòu)劃分,后文在講解分代概念時,我們會提供更加直觀,更加清晰的內(nèi)存結(jié)構(gòu)圖。
3. 什么是堆內(nèi)存
物理層面:從物理層面(硬件層面)來說,當(dāng) Java 程序開始運(yùn)行時,JVM 會從操作系統(tǒng)獲取一些內(nèi)存。JVM 使用這些內(nèi)存,這些內(nèi)存的一部分就是堆內(nèi)存。
Java層面:從開發(fā)層面來說,堆內(nèi)存通常在存儲地址的底層,向上排列。當(dāng)一個對象通過 new 關(guān)鍵字或通過其他方式創(chuàng)建后,對象從堆中獲得內(nèi)存。當(dāng)對象不再使用了,被當(dāng)做垃圾回收掉后,這些內(nèi)存又重新回到堆內(nèi)存中。
總結(jié)來說,堆內(nèi)存是JVM啟動時,從操作系統(tǒng)獲取的一片內(nèi)存空間,他主要用于存放實(shí)例對象本身,創(chuàng)建完成的對象會放置到堆內(nèi)存中。
4. 堆內(nèi)存的分代概念
從上文堆內(nèi)存的結(jié)構(gòu)圖中,我們看到了比較多的JVM堆內(nèi)存中的專有名詞,比如:年輕代,老年代。那么對于堆內(nèi)存來說,分代是什么意思呢?為什么要進(jìn)行分代呢?
分代:將堆內(nèi)存從概念層面進(jìn)行模塊劃分,總體分為兩大部分,年輕代和老年代。從物理層面將堆內(nèi)存進(jìn)行內(nèi)存容量劃分,一部分分給年輕代,一部分分給老年代。這就是我們所說的分代。
分代的意義:易于堆內(nèi)存分類管理,易于垃圾回收。類似于我們經(jīng)常使用的 Windows 操作系統(tǒng),我們會將物理磁盤劃出一部分存儲空間作為用戶系統(tǒng)安裝盤(如 C 盤),我們還極大可能將剩余的磁盤空間劃分為 C, D, E 等磁盤,用于存儲同一類型的數(shù)據(jù)。
-
易于管理:對于堆空間的分代也是如此,比如新創(chuàng)建的對象會進(jìn)入年輕代(YoungGen)的生成區(qū)(Eden),生命周期未結(jié)束的且可達(dá)的對象,在經(jīng)歷多次垃圾回收之后,會存放入老年代(OldGen),這就是分類管理;
-
易于垃圾回收:將對象根據(jù)存活概率進(jìn)行分類,對存活時間長的對象,放到固定區(qū),從而減少掃描垃圾時間及 GC 頻率。針對分類進(jìn)行不同的垃圾回收算法,對算法揚(yáng)長避短。
Tips:關(guān)于上文提到的垃圾回收部分的知識,我們會在后邊的章節(jié)做專門的、詳細(xì)的講解,此處我們先做了解即可。
5. 堆內(nèi)存結(jié)構(gòu)詳解
講解完分代的概念,我們來對堆內(nèi)存中的不同的代,不同的內(nèi)存空間的作用進(jìn)行更加詳細(xì)的講解。講解之前,我們來看下如下示意圖,更加直觀的了解堆內(nèi)存結(jié)構(gòu)。
堆內(nèi)存每個模塊之間的關(guān)系及各自的特點(diǎn)概述如下:
- JVM 內(nèi)存劃分為堆內(nèi)存和非堆內(nèi)存,堆內(nèi)存分為年輕代(YoungGen)、老年代(OldGen);
- 年輕代又分為 Eden 和 Survivor 區(qū)。Survivor 區(qū)由 FromSpace 和 ToSpace 組成。Eden 區(qū)占大容量,Survivor 兩個區(qū)占小容量,默認(rèn)比例是 8:1:1;
- 堆內(nèi)存存放的是對象,垃圾收集器就是收集這些對象,然后根據(jù) GC 算法回收;
- 新生成的對象首先放到年輕代 Eden 區(qū),當(dāng) Eden 空間滿了,觸發(fā) Minor GC,存活下來的對象移動到Survivor0 區(qū),Survivor0 區(qū)滿后觸發(fā)執(zhí)行 Minor GC,Survivor0 區(qū)存活對象移動到 Suvivor1 區(qū),這樣保證了一段時間內(nèi)總有一個 survivor 區(qū)為空。經(jīng)過多次 Minor GC 仍然存活的對象移動到老年代;
- 老年代存儲長期存活的對象,GC 期間會停止所有線程等待 GC 完成,所以對響應(yīng)要求高的應(yīng)用盡量減少發(fā)生 Major GC,避免響應(yīng)超時。
Tips:關(guān)于上文提到的垃圾回收部分的知識,我們會在后邊的章節(jié)做專門的、詳細(xì)的講解,此處我們主要關(guān)注在堆內(nèi)存的每個模塊的概念,特點(diǎn)及作用。對于垃圾回收部分的知識,我們后續(xù)再進(jìn)行學(xué)習(xí)。
6. 小結(jié)
本節(jié)主要講解了運(yùn)行時數(shù)據(jù)區(qū)里邊的堆內(nèi)存,堆內(nèi)存是一塊共享內(nèi)存區(qū)域,在運(yùn)行時數(shù)據(jù)區(qū)占據(jù)著十分重要的位置。我們了解了堆內(nèi)存里的分代概念,并從示意圖中直觀的感受了堆內(nèi)存的結(jié)構(gòu)。我們了解了堆內(nèi)存中不同內(nèi)存空間模塊的作用、特點(diǎn)及意義。這都是非常重要的知識點(diǎn)。
由于垃圾回收絕大多數(shù)都是發(fā)生在堆內(nèi)存中,因此在課程講解的過程中,多少會涉及到垃圾回收的一些概念,此處如果不能理解的學(xué)習(xí)者,可以在學(xué)習(xí)完垃圾回收器后再次理解目前不能夠掌握的知識。