2.2.3 标记法-清除算法
所有存活对象向一端移动,清理另一端。
2.2.4 Hotspot算法实现
1、记录一下内存偏移量进入OopMap,记录点不能是全部的全局性引用、执行上下文,应该是一些safepoiont ,这个安全点主要是方法调用、循环跳转、异常跳转等。
2、虚拟机采用voluntary suspension 主动式中断,主动轮询到安全点,主动挂起进行枚举Gc root。
3、safe region 安全区域是安全点的扩展,离开安全区域进行枚举Gc Root。
2.3 垃圾收集器
2.3.1 Serial 收集器-串行
-新生代串行回收器
(1)Serial特点:
-单线程串行搜集
-独占式垃圾回收
-垃圾回收需要stop the world
-复制算法
-适合cpu等硬件不是很好的场合
(2)参数
-XX:+UseSerialGC 指定新生代使用串行搜集器
-老年代串行回收器
(1)Serial old特点:
-单线程、独占与新生代一样
-标记整理算法
-老年代回收时间比新生代时间更长,应用程序停顿更长
(2)设置参数:
-XX:UseSerialGC 新生代,老年代都使用串行回收器
-XX:UseParNeGC 新生代使用ParNew,老年代使用串行
-XX:UseParalleGC 新生代使用ParallelGC回收,老年代使用串行回收
2.3.2 并行收集器
-新生代-ParNew回收器
(1)特点:
-串行回收变为多线程
-使用复制方法
-GC回收仍然会暂停,但是多线程的效率会提供(多核情况)
(2)设置参数:
-XX:UseParNewGc 新生代使用ParNew回收器,老年代使用串行Serial回收器
-XX:UseConcMarksSweepGc 新生代使用ParNew,老年代使用CMS回收器
-XX:ParallerGCThread=n 新生代回收使用线程数据量,cpu核数小于8的时候,等于cpu的数量,高于8时候, 3+((5*cpu_count)/8)
-新生代-Paraller Scavenge回收器
(1)特点:
-并行回收与ParNew一样,但是更注重吞吐量。
-使用复制算法
-支持自适应GC调节
(2)设置参数:
-XX:UseParallerGC 新生代使用ParallerGC,老年代使用串行回收。
-XX:UseParallerOldGC 新生代使用ParallelGC,老年代使用ParallelOld
-XX:MaxGCPauseMillis=n 最大停顿时间
-XX:GCTimeRatio=n (0-100之间) 设置n%的时间来进行垃圾回收
-XX:UseAdaptiveSizePolicy 自动模式
-老年代ParallelOldGC 回收器
(1)特点:
-与新生代ParallelGC回收器一样,关注老年代的吞吐量。
-使用标记-压缩算法
(2)设置参数:
-XX:UseParallelOldGC 新生代回收器,老年代使用ParallelOldGC回收器
-XX:ParallelGCThreads=n cpu核与新生代一样
-老年代CMS回收器
(1)特点:
-“并发”运行回收
-标记清除算法-有碎片
-CMS主要关注系统停顿时间
-用于大多数B/S服务器,默认线程数量(cpu_count+3)/4
(2)主要步骤:
-1初始化标记、2并发标记、3、预备清理 4、重新标记 5、并发清理 6、并发重置
(3) 设置参数:
-XX:CMSPrecleaningEnabled 关闭预备清理
-XX:UseConcMarksSweepGC 老年代使用CMS,新生代使用ParNew
-XX:ConcGCThreads=n 设置并发线程数
-XX:ParallelCMSTheads=n 设置并发线程数
-XX:CMSInsitiaingOccupancyFeaction=n 1.7默认回收比例我老年代92%
-XX:UseCMSCompactAtFullCollection 开启碎片整理,但是会增加停顿时间
-XX:CMSFullGCsBeforeCompation=n 用于指定进行多少次CMS回收后, 再进行一次内存压缩
-XX:+CMSParallelRemarkEnabled 在使用UseParNewGC 的情况下, 尽量减少 mark 的时间
-XX:+UseCMSInitiatingOccupancyOnly 表示只有达到阀值时才进行CMS回收
-G1回收器
替代CMS的垃圾回收器。
(1)特点:
-并行与并发
-分代收集
-空间整合
-预测停顿
2.3 理解GC日志
[GC (System.gc()) [PSYoungGen: 9339K->800K(76288K)] 9339K->808K(251392K), 0.0014161 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 800K->0K(76288K)] [ParOldGen: 8K->579K(175104K)] 808K->579K(251392K),
[Metaspace: 3032K->3032K(1056768K)], 0.0049896 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
- 1、“[GC” 与 “[Full GC”为这次垃圾回收的停顿类型,如果有Full表示发送了“stop-the-world”
- 2、[GC (System.gc())为调用了System.gc()
- 3、接下来表示垃圾收集器“[DefNew”=“Default New Generation”----》用的是serial收集器
- "[Parnew"="Paraller New Genneration"---》用的是Parnew收集器
- “[PsYoungGen”==》用的 是Parallel Scavenge 收集器
- 3.1 紧接着的”9339K->800K(76288L)“=="GC前内存使用容量->GC后内存使用容量(该内存区域总容量)",
- 4、方括号后面”9339->808K(251392)“=="GC前Java堆使用容量->GC后Java堆使用容量(Java堆总容量)"
- 5、”0.0014161“==”该区域GC所占用的时间,单位为秒“
- 6、"[Times: user=0.00 sys=0.00, real=0.00 secs]"=="用户消耗的CPU时间,内核消耗的CPU时间,真正从头到尾的时间"
2.4 垃圾回收常用参数
参 数 |
描 述 |
UseSerialGC |
虚拟机运行在Client 模式下的默认值,打开此开关后,使用Serial +Serial Old 的收集器组合进行内存回收 |
|
UseParNewGC |
打开此开关后,使用ParNew + Serial Old 的收集器组合进行内存回收 |
UseConcMarkSweepGC |
打开此开关后,使用ParNew + CMS + Serial Old 的收集器组合进行内存 |
回收。Serial Old 收集器将作为CMS 收集器出现Concurrent Mode Failure失败后的后备收集器使用 |
UseParallelGC |
虚拟机运行在Server 模式下的默认值,打开此开关后,使用Parallel |
Scavenge + Serial Old(PS MarkSweep)的收集器组合进行内存回收 |
UseParallelOldGC |
打开此开关后,使用Parallel Scavenge + Parallel Old 的收集器组合进行内存回收 |
SurvivorRatio |
新生代中Eden 区域与Survivor 区域的容量比值, 默认为8, 代表 |
Eden :Survivor=8∶1 |
PretenureSizeThreshold |
直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象 |
将直接在老年代分配 |
MaxTenuringThreshold |
晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC 之后,年 |
龄就加1,当超过这个参数值时就进入老年代 |
UseAdaptiveSizePolicy |
动态调整Java 堆中各个区域的大小以及进入老年代的年龄 |
HandlePromotionFailure |
是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个 |
Eden 和Survivor 区的所有对象都存活的极端情况 |
ParallelGCThreads |
设置并行GC 时进行内存回收的线程数 |
GCTimeRatio |
GC 时间占总时间的比率,默认值为99,即允许1% 的GC 时间。仅在 |
2.5内存分配与回收策略
2.5.1 对象优先在Eden 分配
实例代码:
private static final int _1MB=1024*1024;
/**
* -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
* java堆大小为20M 10M分配给新生代 10M分配老年代
* 新生代中国eden与suivivor区比例为8:1
* -发现新生代已经用了6M+souvivor@2M=8M,
* 不够分配最后的4M,所以发生一次GC,老年代转移4M空间-40% 可以发现正确性
*/
public static void testAllocation(){
byte[] allocation1,allocation2,allocation3,allocation4;
allocation1=new byte[2*_1MB];
allocation2=new byte[2*_1MB];
allocation3=new byte[2*_1MB];
allocation4=new byte[4*_1MB];//出现一次minor gc 新生代gc
}
public static void main(String[] args) {
testAllocation();
}
GC信息:
[GC (Allocation Failure) [PSYoungGen: 6359K->818K(9216K)] 6359K->4922K(19456K), 0.0031997 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 7284K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 78% used [0x00000007bf600000,0x00000007bfc50508,0x00000007bfe00000)
from space 1024K, 79% used [0x00000007bfe00000,0x00000007bfeccb90,0x00000007bff00000)
to space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
ParOldGen total 10240K, used 4104K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 40% used [0x00000007bec00000,0x00000007bf002020,0x00000007bf600000)
Metaspace used 3023K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 329K, capacity 388K, committed 512K, reserved 1048576K
2.5.2 大对象直接进入老年代
实例代码:
package com.ycy.java.gc;
/**
* @Copyright © 2017 . All rights reserved. Created with IntelliJ IDEA.
* @project: untitled
* @Package: com.ycy.java.gc
* @Description:
* @autho: ycy
* @Date: 2017-10-19
* @Time: 23:13
*/
public class TestGc {
private static final int _1MB=1024*1024;
/**
* vgs: -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145700
* object space 10240K, 60% 直接进入老年代,因为大于设置的3145700
* Server模式下默认:Serial old+Parallel Scavenger
* -XX:PretenureSizeThreshold=3145700不认识这个参数,但是一样大对象进入老年区
*/
public static void testPretenureSizeThreshold(){
byte[] allocation;
allocation=new byte[6*_1MB];//老年代中
}
public static void main(String[] args) {
//对象优先分配Eden
// testAllocation();
//大对象直接进入老年代
testPretenureSizeThreshold();
}
}
GC信息:
Heap
PSYoungGen total 9216K, used 2450K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 29% used [0x00000007bf600000,0x00000007bf864bb8,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
ParOldGen total 10240K, used 6144K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 60% used [0x00000007bec00000,0x00000007bf200010,0x00000007bf600000)
Metaspace used 3022K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 329K, capacity 388K, committed 512K, reserved 1048576K
2.5.3 长期存活的对象进入老年代
如果对象在Eden出生到一次Minor GC新生代GC之后仍然存活,并且在Survivor中容纳,移到Survivor,这个时候年龄1岁,默认阀值为15岁,就会移动到老年代。
示例代码:
/**
* -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=0
* 修改-XX:MaxTenuringThreshold=15 观察对象存在新生代
*/
public static void testTenuringThreshold(){
byte[] allocation1,allocation2,allocation3,
allocation4,allocation5,allocation6;
allocation1=new byte[1*_1MB];
allocation2=new byte[1*_1MB];
allocation3=new byte[1*_1MB];
allocation4=new byte[1*_1MB];
allocation5=new byte[1*_1MB];
allocation5=null;
allocation5=new byte[1*_1MB];
}
GC结果 :XX:MaxTenuringThreshold=0
from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
GC结果 :XX:MaxTenuringThreshold=15
from space 1024K, 75%
2.5.4 长期存活的对象进入老年代
相同年龄对象内存之和大于Survivor的一半,年龄大于或者等于该年龄的对象,直接进入老年代。
上面代码蒋年龄限制设置1-15之间,你会发现,对象直接进入老年代。
object space 10240K, 55% used
2.5.5 长期存活的对象进入老年代
1.6之后,老年代的连续空间大于新生代的对象的总大小,或者历次晋升的平均大小,进行Minor GC ,否则full GC。意思就是老年代大于新生代或进行一次Minor GC。
三、总结
主要讲解垃圾回收器的特点和运作原理,验证了一些虚拟机 分配原则。