笔记:深入理解JVM 第3章 垃圾回收器与内存分配策略
1、對(duì)象是否已死
(1). 引用計(jì)數(shù)法:無法回收相互引用的對(duì)象,故JVM沒有采用
例子:?
public class ReferenceObj { public ReferenceObj refObj; } public static void main(String[] args){ReferenceObj obj1 = new ReferenceObj(); ReferenceObj obj2 = new ReferenceObj(); obj1.refObj = obj2; obj2.refObj = obj1; obj1 = null; obj2 = null; }以上例子使用引用計(jì)數(shù)法無法回收,但是JVM使用的不是,JVM可回收。
(2). 可達(dá)性分析算法:通過一系列“GC root” 作為起始點(diǎn),從這些節(jié)點(diǎn)開始往下搜索,搜索經(jīng)過的路徑成為引用鏈。若對(duì)象不與引用鏈相連接,則該對(duì)象不可用,可被回收。
GC root的對(duì)象有:
a. 虛擬機(jī)棧中引用的對(duì)象
b. 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
c. 方法區(qū)中常量引用的對(duì)象
d. 本地方法棧中JNI引用的對(duì)象
(3).引用的類型
強(qiáng)引用:引用存在則不會(huì)回收
軟引用:內(nèi)存不足,則回收
弱引用:不管內(nèi)存是否足夠,必定回收
虛引用:無法通過虛引用獲得對(duì)象,只為在回收之前獲得系統(tǒng)通知
(4).finalize 方法可使得對(duì)象在回收之前復(fù)活,但是不建議使用
(5). 方法區(qū)(持久代)回收
方法區(qū)中無用的常量、無用的類會(huì)被回收。判斷無用的類的準(zhǔn)則,,同時(shí)滿足:
a.該類的所有實(shí)例已經(jīng)回收
b.該類的ClassLoader已經(jīng)回收
c.該類的Class 對(duì)象不再被引用。
JVM 通過設(shè)置 -Xnoclaagc 控制。
在大量使用反射、動(dòng)態(tài)代理、CGLib等框架,動(dòng)態(tài)生成JSP、OSGI等頻繁定義ClassLoader場景下都需要JVM具備卸載類的功能,保證方法區(qū)不溢出。
2、垃圾回收算法
(1).標(biāo)記-清除算法
最簡單的算法。
不足:效率低;內(nèi)存產(chǎn)生大量碎片。
(2). 復(fù)制算法 (?新生代)
兩塊內(nèi)存,每次使用一塊,回收時(shí)候?qū)?duì)象從塊A移到塊B,再清理塊A。
優(yōu)點(diǎn):簡單、高效,無碎片。
缺點(diǎn):內(nèi)存使用率低。
新生代一般用的是復(fù)制算法。因?yàn)樾律袑?duì)象98%是“朝生夕死”,所以兩塊內(nèi)存不需要1:1,而是使用1塊比較大的Eden和2塊比較小的Survivor。回收時(shí),將Eden和Survivor A中還存活的數(shù)據(jù)移動(dòng)到Survivor B,再清理Eden 和 ?Survivor A;再次回收時(shí)候,將Eden和Survivor B中還存活的數(shù)據(jù)移動(dòng)到Survivor A,再清理Eden 和 ?Survivor B。JVM 默認(rèn)Eden 和 Survivor的比例是8:1, 即Eden: Survivor A: Survivor B ?= ?8 :0.5:0.5 。當(dāng)回收時(shí)候 Survivor 不夠用時(shí)候, 對(duì)象將被移動(dòng)到老生代。
(3). 標(biāo)記-整理算法 (老生代)
先標(biāo)記,再讓所有存活的對(duì)象向一端移動(dòng),然后直接清除端邊界一外的內(nèi)存。
(4). 分代收集算法
也就是新生代用 “復(fù)制" 算法,老生代用 “標(biāo)記-整理” 或者 "標(biāo)記-清除" 算法。
?
3、HotSpot 的JVM 算法的實(shí)現(xiàn)
GC 導(dǎo)致必須停頓所有Java執(zhí)行進(jìn)程的原因:枚舉根節(jié)點(diǎn),作可達(dá)性分析。4、垃圾收集器
(1). ?Serial ?收集器??(新生代)
基于復(fù)制算法。
只使用單線程,在垃圾收集時(shí)候,必須暫停其他所有線程,直到其收集結(jié)束 (stop the world)。Serial Collector 是Client模式下,新生代的默認(rèn)收集器。
(2). ParNew 收集器 (Parallel New 收集器) (新生代)?
基于復(fù)制算法。
是Serial Collector 的多線程版本,也必須暫停其他所有線程。?ParNew Collector 是Server模式下,新生代的默認(rèn)收集器。使用 -XX: UseParNewGC 打開。
當(dāng)老生代使用 -XX:+UseConcMarkSweepGC 時(shí)候,新生代必須使用?Serial Collector 或 ?ParNew Collector ,默認(rèn)是?ParNew Collector ?。
默認(rèn)開啟的線城數(shù)目與CPU核數(shù)目一致。
(3). Parallel Scavenge 收集器 ?(新生代)
基于復(fù)制算法。
與ParNew Collector 相比,可以控制最大垃圾收集停頓時(shí)間-XX:MaxGCPauseMillis 和 吞吐量-XX: GCTimeRadio,-XX:
最大垃圾收集停頓時(shí)間-XX:MaxGCPauseMillis=500:停頓時(shí)間越短,則垃圾回收越頻繁。
吞吐量-XX:GCTimeRadio=99:吞吐量 = (用戶線程使用CPU時(shí)間) / (用戶線程使用CPU時(shí)間 + GC線程使用CPU時(shí)間)
自適應(yīng)調(diào)節(jié)策略 -XX:+UseAdaptiveSizePolicy: 開啟后系統(tǒng)會(huì)使用當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整停頓時(shí)間和吞吐量。
(4). Serial Old 收集器 ?(老生代)
基于標(biāo)記-整理算法。
Serial 收集器的老生代版本。三個(gè)方面用途:
a. Client 模式下用
b. Server 模式下老版本的JVM 與Parallel Scavenge 搭配使用
c. ?作為老生代CMS的后備方案,失敗的時(shí)候使用。
(5). Parallel Old 收集器
基于“標(biāo)記-整理”算法。
Parallel Scavenge 的老生代版本。一般用于 與新生代的?Parallel Scavenge 相搭配,整個(gè)系統(tǒng)吞吐量優(yōu)先。
(6). CMS 收集器
基于“標(biāo)記-清除”
Concurrent Mark Sweep ,以獲取最短時(shí)間停頓為目標(biāo)的收集器。互聯(lián)網(wǎng)站和B/S系統(tǒng)的服務(wù)端,一般都用CMS,以保證服務(wù)的響應(yīng)速度,停頓時(shí)間短,給用戶帶來較好地體驗(yàn)。
包括4個(gè)步驟:初始標(biāo)記、并發(fā)標(biāo)記、初始標(biāo)記、并發(fā)清除。
優(yōu)點(diǎn):并發(fā)收集、低停頓。
缺點(diǎn):
a. 對(duì)CPU資源敏感,即占用大量CPU資源,不會(huì)導(dǎo)致用戶線程停頓但是會(huì)變慢;
b. 無法處理浮動(dòng)垃圾,即在清理階段產(chǎn)生的垃圾,這些垃圾必須在下一次GC時(shí)候回收,所以還必須預(yù)留部分空間,設(shè)置-XX:CMSInitiatingOccupancyFraction 來提前觸發(fā) (默認(rèn) 92% 觸發(fā));
c. ?收集結(jié)束會(huì)產(chǎn)生空間碎片,設(shè)置+UseCMSCompactAtFullCollection 整理空間碎片。
(7) G1 收集器 ?(新生代、老生代)
Garbage First ,未來可能替換CMS。特點(diǎn):
a. 并發(fā)與并行
b. 分代收集
c.空間整合,使用“標(biāo)記-整理”算法,不會(huì)產(chǎn)生空間碎片
d.可預(yù)測的停頓
現(xiàn)在還沒有廣泛運(yùn)用。
5、配置參數(shù)總結(jié)
-XX:+UseSerialGC ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??使用?Serial +?Serial Old 收集器
-XX:+UseParNewGC? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 使用ParNew + Serial Old 收集器
-XX:+UseConcMarkSweepGC? ? ? ? ? ? ? ? ? ? ? 使用ParNew + CMS + Serial Old (失敗后的預(yù)備方法)
-XX:+UseParallGC? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 使用 Parallel Scavenge + ?Serial Old?
-XX:+UseParallelOldGC ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??使用?Parallel Scavenge + ?Parallel Old
-XX:SurvivorRatio=8 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?新生代中Eden 與 Survivor 的比例,默認(rèn)8
-XX:PretenureSizeThreshold=1024 ? ? ? ? ? ?大于此參數(shù)的對(duì)象直接分配在老生代
-XX:MaxTenuringThreshold=5 ? ? ? ? ? ? ? ? ??新生代中對(duì)象年齡超過此值,移動(dòng)到老生代
-XX:+UseAdaptiveSizePolicy ?? ? ? ? ? ? ? ? ? ? ? 動(dòng)態(tài)調(diào)整Java 堆中各區(qū)域大小 以及進(jìn)入老生代的年齡
-XX:+HandlePromotionFailure ? ? ? ? ? ? ? ? ? ??是否允許分配擔(dān)保失敗
-XX:ParallelGCThreads=10 ? ? ? ? ? ? ? ? ? ? ? ??GC并行線程個(gè)數(shù)
-XX:GCTimeRatio=98 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GC吞吐量,僅在Parallel Scavenge 有效
-XX:MaxGCPauseMillis=500? ? ? ? ? ? ? ? ? ? ? ? GC最大停頓時(shí)間,僅在Parallel Scavenge 有效
-XX:CMSInitiatingOccupancyFraction=70 ? ?CMS在使用70%觸發(fā), 默認(rèn)68%
-XX:+UseCMSCompactAtFullCollection ?? ? ? ?CMS后進(jìn)行內(nèi)存整理
-XX:CMSFullGcsBeforeCompaction=5 ? ? ? ??CMS 5次后進(jìn)行碎片整理
6. ?理解GC日志
[GC ? ? ? ? ?:GC 沒有發(fā)生Stop-The-World停頓
[Full GC ? :GC 發(fā)生了Stop-The_World
[Full GC(System) : ?GC 由System.gc() 調(diào)用
[DefNew ? : 新生代使用了默認(rèn)的Serial 收集器 Default New Generation
[Tenured: ? 老生代 ?Tenured?Generation
[Perm: ? 持久帶 ?Perm Generation
[ParNew: ?新生代使用ParNew收集器 Parallel New Generation
[PSYoungGen: ?新生代使用Parallel Scavenge 收集器?
3324K ->152K (3712K) : ?GC前為3324K -> GC后為152k (總?cè)萘繛?712K)
[Times: user=0.01, sys=0.00 , real=0.02secs] ?: 用戶態(tài)消耗的CPU時(shí)間、內(nèi)核態(tài)消耗的CPU時(shí)間、墻鐘時(shí)間。墻鐘時(shí)間包括CPU時(shí)間與非運(yùn)算的等待消耗,如等待磁盤IO、等待線程阻塞。
7、內(nèi)存分配與回收策略
(1)、對(duì)象優(yōu)先在Eden分配
大多數(shù)情況下,對(duì)象在新生代的Eden區(qū)分配,當(dāng)Eden區(qū)沒有足夠的空間進(jìn)行分配適合,虛擬機(jī)會(huì)進(jìn)行一次 Minor GC。
配置:?
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8代碼:
public class MinorGCTest {private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] allocate1, allocate2, allocate3, allocate4;allocate1 = new byte[2*_1MB];allocate2 = new byte[3*_1MB];allocate3 = new byte[3*_1MB];allocate4 = new byte[4*_1MB];} }輸出:?
[GC [PSYoungGen: 5792K->600K(9216K)] 5792K->5720K(19456K), 0.0022215 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]?
[Full GC [PSYoungGen: 600K->0K(9216K)] [ParOldGen: 5120K->5589K(10240K)] 5720K->5589K(19456K) [PSPermGen: 2508K->2507K(21504K)], 0.0091615 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]?
Heap
?PSYoungGen ? ? ?total 9216K, used 6727K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
? eden space 8192K, 82% used [0x00000000ff600000,0x00000000ffc91c28,0x00000000ffe00000)
? from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
? to ? space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
?ParOldGen ? ? ? total 10240K, used 5589K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
? object space 10240K, 54% used [0x00000000fec00000,0x00000000ff1754e0,0x00000000ff600000)
?PSPermGen ? ? ? total 21504K, used 2517K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
? object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c75520,0x00000000faf00000)
(2)、大對(duì)象直接進(jìn)入老生代
可通過 -XX:PretenureSizeThreshold 設(shè)置。寫程序應(yīng)該避免大對(duì)象。
配置:
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728代碼: public class PretenureSizeThresholdTest {private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] allocate1;allocate1 = new byte[8*_1MB];} }輸出:?
Heap
?PSYoungGen ? ? ?total 9216K, used 835K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
? eden space 8192K, 10% used [0x00000000ff600000,0x00000000ff6d0fc0,0x00000000ffe00000)
? from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
? to ? space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
?ParOldGen ? ? ? total 10240K, used 8192K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
? object space 10240K, 80% used [0x00000000fec00000,0x00000000ff400010,0x00000000ff600000)
?PSPermGen ? ? ? total 21504K, used 2515K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
? object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c74cd0,0x00000000faf00000)
(3)、長期存活的對(duì)象移動(dòng)到老生代
通過-XX:MaxTenuringThreshold 設(shè)定。?
配置
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC 代碼: private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] allocate1, allocate2, allocate3, allocate4;allocate1 = new byte[ _1MB/4];allocate2 = new byte[4*_1MB];allocate3 = null;allocate3 = new byte[4*_1MB]; }輸出:
[GC[ParNew: 5024K->758K(9216K), 0.0021874 secs] 5024K->4856K(19456K), 0.0022406 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]?
Heap
?par new generation ? total 9216K, used 5346K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
? eden space 8192K, ?56% used [0x00000000f9a00000, 0x00000000f9e7af60, 0x00000000fa200000)
? from space 1024K, ?74% used [0x00000000fa300000, 0x00000000fa3bdaa0, 0x00000000fa400000)
? to ? space 1024K, ? 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
?concurrent mark-sweep generation total 10240K, used 4098K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
?concurrent-mark-sweep perm gen total 21248K, used 2519K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
?
(4)、動(dòng)態(tài)對(duì)象年齡判定
為了能更好地適應(yīng)不同程序的內(nèi)存情況,虛擬機(jī)并不是永遠(yuǎn)地要求對(duì)象的年齡必須達(dá)到?MaxTenuringThreshold 才進(jìn)入老生代。如果在Survivor 空間中相同年齡所有對(duì)象大小的總和大于Survivor 空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老生代,無需等到MaxTenuringThreshold?。
(5)、空間分配擔(dān)保
在發(fā)生Minor GC 之前, 虛擬機(jī)會(huì)先檢查老生代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果成立,則Minor GC可用確保是安全的。在JDK 1.6_24 之后,只要大于,則進(jìn)行Minor GC, 否則則進(jìn)行Full GC。?
8. 新生代GC 和 老生代GC 各自特點(diǎn)
新生代GC:Minor GC,因?yàn)樾律膶?duì)象大多數(shù)朝生夕滅,所以非常頻繁,回收速度也很快。
老生代GC:Major GC/Full GC, ?其速度比 Minor GC 慢10倍以上。
轉(zhuǎn)載于:https://www.cnblogs.com/leeeee/p/7276309.html
總結(jié)
以上是生活随笔為你收集整理的笔记:深入理解JVM 第3章 垃圾回收器与内存分配策略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++中函数模板template和函数参
- 下一篇: jquery ajax异步调用