前言
在這篇文章介紹并記錄關(guān)于JVM內(nèi)存結(jié)構(gòu)的簡單應(yīng)用學(xué)習(xí)。基于JDK8。
1.JVM內(nèi)存結(jié)構(gòu)
1.1運(yùn)行時(shí)數(shù)據(jù)區(qū)

1.2 程序計(jì)算器PC Register
JVM支持多線程同時(shí)執(zhí)行,每一個(gè)線程都有自己的PC Register,線程正在執(zhí)行的方法叫做當(dāng)前方法,如果是Java代碼,PC Register 里面存放的就是當(dāng)前正在執(zhí)行的指令地址,如果是C代碼,則為空
1.3 虛擬機(jī)棧JVM Stacks
Java 虛擬機(jī)棧(Java Virtual Machine Stacks)是線程私有的,它的,生命周期與線程是相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程。
1.4 堆Heap
Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中是最大的一塊。堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存。
Java堆可以處理物理上不連續(xù)的內(nèi)存空間中,只要邏輯上市連續(xù)的即可。
1.5 方法區(qū)Method Area
方法區(qū)與Java堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它卻有一個(gè)別名叫做No-Heap(非堆),目的是與Java堆區(qū)分開來。
1.6 常量池Run-Time Constant Pool
運(yùn)行時(shí)常量池(Run-Time Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外、還有一項(xiàng)信息是常量池( Constant Pool Table),用于存放編譯期生成的各種字面量和符號(hào)引用(int、數(shù)組),這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。
1.7 本地方法棧Native Method Statcks
本地方法棧與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,它們之間的區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機(jī)使用到Native方法服務(wù)。
1.8 JVM 內(nèi)存結(jié)構(gòu)

1.8.1 年輕代
所有新生成的對(duì)象首先都是放在年輕代的。年輕代的目標(biāo)就是盡可能快速的收集掉那些生命周期短的對(duì)象。年輕代分三個(gè)區(qū)。一個(gè)Eden區(qū),兩個(gè) Survivor區(qū)(一般而言)。大部分對(duì)象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時(shí),還存活的對(duì)象將被復(fù)制到Survivor區(qū)(兩個(gè)中的一個(gè)),當(dāng)這個(gè) Survivor區(qū)滿時(shí),此區(qū)的存活對(duì)象將被復(fù)制到另外一個(gè)Survivor區(qū),當(dāng)這個(gè)Survivor去也滿了的時(shí)候,從第一個(gè)Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對(duì)象,將被復(fù)制“年老區(qū)(Tenured)”。需要注意,Survivor的兩個(gè)區(qū)是對(duì)稱的,沒先后關(guān)系,所以同一個(gè)區(qū)中可能同時(shí)存在從Eden復(fù)制過來對(duì)象,和從前一個(gè)Survivor復(fù)制過來的對(duì)象,而復(fù)制到年老區(qū)的只有從第一個(gè)Survivor去過來的對(duì)象。而且,Survivor區(qū)總有一個(gè)是空的。同時(shí),根據(jù)程序需要,Survivor區(qū)是可以配置為多個(gè)的(多于兩個(gè)),這樣可以增加對(duì)象在年輕代中的存在時(shí)間,減少被放到年老代的可能。
1.8.2 年老代
在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到年老代中。因此,可以認(rèn)為年老代中存放的都是一些生命周期較長的對(duì)象。
1.8.3 持久代
用于存放靜態(tài)文件,如今Java類、方法等。持久代對(duì)垃圾回收沒有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class,例如Hibernate 等,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來存放這些運(yùn)行過程中新增的類。持久代大小通過-XX:MaxPermSize=<N>進(jìn)行設(shè)置。
1.8.4 Metaspace
Metaspace會(huì)有Class、Package、Method、Field、字節(jié)碼、常量池、符號(hào)引用等等。
CSS:32位指針的Class
CodeCache:JIT編譯后的本地代碼、JNI使用的C代碼
1.9 壓縮站空間
壓縮站空間指的是CCS,默認(rèn)是存在壓縮站空間。

1.9.1 演示禁用壓縮站
禁用后就看到不CCSC CCSU了,只顯示為0了
vim catalina.sh

1.9.2 常用參數(shù)
//堆大小
-Xms -Xmx
//新生代大小,最大的大小
-XX:NewSize -XX:MaxNewSize
// 新生代和老年帶的比例, Eden和survivo比例大小
-XX:NewRatio -XX:SurvivorRatio
//元空間
-XX:MetaspaceSize -XX:MaxMetaspaceSize
//開啟壓縮站 啟動(dòng)有CCS
-XX:+UseCompressedClassPointers
// 沒用CCS
-XX:CompressedClassPointers
-XX:InitialCodeCacheSize // CodeCache最初大小
-XX:ReservedCodeCacheSize // 最大的大小 根據(jù)不用調(diào)
2 垃圾回收算法
思想:枚舉根節(jié)點(diǎn),做可達(dá)性分析
根節(jié)點(diǎn):類加載器、Thread、虛擬機(jī)棧的本地變量表、static成員、常量引用、本地方法棧的變量等等。
2.1 標(biāo)記清除
算法分為"標(biāo)記"和"清除"兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有。
缺點(diǎn):效率不高。標(biāo)記和清除兩個(gè)過程的效率都不高。產(chǎn)生碎片。碎片太多會(huì)導(dǎo)致會(huì)提前GC。
2.2 復(fù)制
算法:它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次 只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另一塊上面,然后再把已經(jīng)使用的內(nèi)存空間一次清理掉。
優(yōu)缺點(diǎn):
實(shí)現(xiàn)簡單,運(yùn)行效率高,但是空間利用率低。
2.3 標(biāo)記整理
算法:標(biāo)記過程仍然與"標(biāo)記-清除"算法一樣,但后續(xù)步驟不是直接對(duì)可回收的對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。
優(yōu)缺點(diǎn): 沒有了內(nèi)存碎片,但是整理內(nèi)存比較耗時(shí)。
2.3 分帶垃圾回收
Young區(qū)復(fù)制算法
Old區(qū)用標(biāo)記清除或者標(biāo)記整理
2.4 對(duì)象分配
對(duì)象優(yōu)先在Eden分配
大對(duì)象直接進(jìn)入老年代: -XX:PretenureSizeThreshold
長期存活對(duì)象直接老年代: -XX:MaxTenuringThreshold
-XX:+PrintTenuringDistribution -XX:TargetSurvivorRatio
3 并行VS并發(fā)
并行(Parallel): 指多條垃圾收集線程并行工作,但此時(shí)用戶線程仍然處于等待狀態(tài);
適合科學(xué)計(jì)算、后臺(tái)處理等弱交互場(chǎng)景。
并發(fā)(Concurrent):指用戶線程與垃圾收集線程同時(shí)執(zhí)行(但不一定是并行的,可能會(huì)交替執(zhí)行),用戶程序在繼續(xù)運(yùn)行,而垃圾收集程序運(yùn)行于另一個(gè)CPU上。
適合對(duì)響應(yīng)時(shí)間有要求的場(chǎng)景,比如Web;
3.1 停頓時(shí)間 VS 吞吐量
停頓時(shí)間: 垃圾收集器做垃圾回收中斷應(yīng)用執(zhí)行 時(shí)間。XX:MaxGCPauseMillis
吞吐量: 花在垃圾收集的時(shí)間和花在應(yīng)用時(shí)間 占比。-XX:GCTimeRatio=<n>,垃圾收集時(shí)間占:1/1+n
3.2 串行收集器
-XX:UseSerialGC -XX:+UseSerialOldGC
3.3 并行收集器
3.3.1 吞吐量優(yōu)先
-XX:+UseParallelGC, -XX:+UseParallelOldGC
3.3.2 查看并行收集器

3.4 并發(fā)收集器
3.4.1 相應(yīng)時(shí)間優(yōu)先
CMS: XX:+UseConcMarkSweepGC -XX:+UseParNewGC
3.4.2 開啟CMS垃圾回收器命令

3.4.2 查看

3.4.3 垃圾收集器搭配

3.4.3 如何選擇垃圾收集器
優(yōu)先調(diào)整堆的大小讓服務(wù)器自己來選擇
如果內(nèi)存小于100M,使用串行收集器
如果是單核,并且沒有停頓時(shí)間的要求,串行或者JVM自己選
如果允許停頓時(shí)間超過1秒,選擇并行或者JVM自己選
如果相應(yīng)時(shí)間最重要,并且不能超過1秒,使用并發(fā)收集器
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html
3.5 Parallel Conllector
-XX:+UseParallelGC 手動(dòng)開啟 , Server模式開啟
-XX:ParallelGCThreads=<N>多少個(gè)GC線程
CPU > 8 N = 5/8
CPU < 8 N = CPU
3.6 Parallel Conllector Ergonomics
-XX:MaxGCPauseMillis=<N>
-XX:GCTimeRatio=<N>
-Xmx<N>
3.7 動(dòng)態(tài)內(nèi)存調(diào)整
-XX:YoungGenerationSizelncrement = <Y>
-XX:TenuredGenerationSizeIncrement=<T>
-xx:AdaptiveSizeDecrementScaleFactor=<D>
3.8 CMS Conllector
- 并發(fā)收集
- 低停頓,低延遲
- 老年代收集器
4 CMS垃圾收集過程
- CMS initial mark : 初始標(biāo)記Root,STW
- CMS concurrent mark :并發(fā)標(biāo)記
- CMS-concurrent-preclean: 并發(fā)預(yù)清理
- CMS remark : 重新標(biāo)記,STW
- CMS concurrent sweep: 并發(fā)清除
- CMS-concurrent-rest:并發(fā)重置
4.1 CMS 缺點(diǎn)
- CPU敏感
- 浮動(dòng)垃圾
- 空間碎片
4.2 CMS 相關(guān)參數(shù)
-XX:ConcGCThreads:并發(fā)的GC線程數(shù)
-XX:+UseCMSCompactAtFullCollection:FullGC之后做壓縮
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后壓縮一次
-XX:CMSlnitiatingOccupancyFraction:觸發(fā)FullGC
-XX:+UseCMSlnitiatingOccupancyOnly:是否動(dòng)態(tài)調(diào)
XX:+CMSScavengeBeforeRemark:FullGC之前做YGC
-XX:+CMSClassUnloadingEnabled:啟動(dòng)回收Perm區(qū)
4.3 ICMS
使用于單核或雙核 不用
5 G1 Collector
基于JDK8后的版本、JDK11可能是geely Collector
簡介
G1(Garbage-First )收集器是一種server-style 回收器,主要面向多核,大內(nèi)存的服務(wù)器。G1 在實(shí)現(xiàn)高吞吐的同時(shí),也最大限度滿足了GC 停頓時(shí)間可控的目標(biāo)。在Oracle JDK7 update 4 及 以后的版本全面支持G1 回收器功能。G1收集器主要為有如下需求的程序設(shè)計(jì):
可以像CMS 收集器 能同時(shí)和應(yīng)用線程 一起并發(fā)的執(zhí)行;
實(shí)現(xiàn)壓縮空間時(shí)用更少的停頓時(shí)間;
滿足可預(yù)測(cè)的GC停頓時(shí)間需求;
不要犧牲太多的吞吐性能;
不需要占用更多的Java Heap;
未來 G1 計(jì)劃要全面取代CMS的。G1相比CMS有更多的優(yōu)勢(shì),G1是壓縮型收集器,G1通過依賴regions分區(qū),可以實(shí)現(xiàn)壓縮更充分。這樣消除大部分潛在的碎片問題。G1提供更精準(zhǔn)的可預(yù)測(cè)的垃圾停頓時(shí)間,滿足用戶指定垃圾回收時(shí)間的需求。
分為三個(gè)固定內(nèi)存大小的部分:、young區(qū),old區(qū),permanent區(qū)。

5.1 G1的幾個(gè)概念
- Region 不用
- Snapshot-The-Beginning(SATB):它是通過Root Tracting得到的,GC開始時(shí)候存活對(duì)象的快照。
- Rset:它的意思就是region B的一個(gè)card里有引用指向region A。所以對(duì)region A來說,該RSet記錄的是points-into的關(guān)系;而card table仍然記錄了points-out的關(guān)系。
5.2 YoungGC
- 新對(duì)象進(jìn)入Eden區(qū)
- 存活對(duì)象拷貝到Surivior區(qū)
- 存活時(shí)間達(dá)到年齡后,對(duì)象就去到Old區(qū)
5.3 MixedGC
- 不是FullGC,回收所有Young和部分Old
- global concurrent marking
5.4 global concurrent marking
- lnitial marking phase:標(biāo)記GC ROOt,STW
- Root region scanning phase:標(biāo)記存活Region
- Concurrent marking phase:暴擊存活對(duì)象
- Remark phase:重新標(biāo)記,STW
- Cleanup phase:部分STW
5.5 MixedGC 時(shí)機(jī)
- lnitiatingHeapOccupancyPercent:堆占有率達(dá)到這個(gè)數(shù)值則觸發(fā)global concurrent marking,默認(rèn)45%
- G1HeapWastePercent:在globalxxxxx結(jié)束之后,可以知道有多少空間被回收,在每次YGC之后和再次發(fā)生Mixed GC之前,會(huì)檢查安垃圾占比會(huì)否達(dá)到此參數(shù),只要達(dá)到了,下次才會(huì)發(fā)生Mixed GC。
5.6 MixedGC 相關(guān)參數(shù)
- G1MixedGCLiveThresholdPercent:Old區(qū)的region被回收時(shí)候的存活對(duì)象占比(百分之八十)
- G1MixedGCCountTarget:一次global conxxx之后,最多執(zhí)行MixedGC的次數(shù)
- G1OldCsetRegionThresholdPercent:一次MixedGC中能被選入CSet的最多old區(qū)Region數(shù)量
5.6 常用參數(shù)
-XX:+UseG1GC //開啟G1
-XX:G1HeapRegionSize=n,//region的大小,1-32M,2048個(gè)
-XX:MaxGCPauseMillis=200 //最大停頓時(shí)間
-XX:ParallelGCThreads=n //SWT線程數(shù)
-XX:ConcGCThreads=n //并發(fā)線程數(shù)量=1/4*并行
5.7 最佳實(shí)踐
- 年輕代大小: 避免使用-Xmn、-XX:NewRatio等顯示設(shè)置Young區(qū)大小,會(huì)覆蓋暫停時(shí)間目標(biāo)。
- 暫停時(shí)間目標(biāo):暫停時(shí)間不要太嚴(yán)苛,其吞吐量目標(biāo)是90%的應(yīng)用程序時(shí)間和10% 垃圾回收時(shí)間,太嚴(yán)苛?xí)苯佑绊懙酵掏铝?/li>
5.8 關(guān)于MixGC調(diào)優(yōu)
-XX:lnitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent
-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent
5.9 關(guān)于是否需要切換到G1
- 50%以上的堆被存活對(duì)象占用
- 對(duì)象分配和晉升的速度變化非常大
- 垃圾回收時(shí)間特別長,超過了1秒
對(duì)于本次章節(jié)是對(duì)JVM入門之內(nèi)存結(jié)構(gòu)與調(diào)優(yōu)介紹記錄和學(xué)習(xí)。