文章目录
垃圾回收器
1.串行:单线程,堆内存较小,适合个人电脑
2.吞吐量优先:多线程,堆内存较大,多核 cpu,让单位时间内,STW的时间最短(单位时间1h内,若垃圾回收2次,0.2s,0.2s,=0.4s)
3.响应时间优先:多线程,堆内存较大,多核 cpu,尽可能让单次的STW的时间最短,若垃圾回收5次,每次的暂停时间是0.1s (0.1s,0.1s,0.1s,0.1s,0.1s,=0.5s)
1. 串行
//使用Serial收集器和SerialOld收集器
-XX:+UseSerialGC = Serial + SerialOld
2. 吞吐量优先(并行操作)
垃圾回收的时间和总时间的占比
固定时间内,垃圾回收时间=每次垃圾回收所用的时间 * n次垃圾回收
如:1小时固定,n=5;1:0.2s;2:0.4s;3:0.3s;4:0.5s;5:0.3s;垃圾回收时间 1.7s
//使用ParallelGC
-XX:+UseParallelGC ~ -XX:+UseParallelOldGC
//采用自适应的调整大小参数(Eden区和Survivor From区的大小,阈值)
-XX:+UseAdaptiveSizePolicy
//调整吞吐量的大小,即垃圾回收的时间和总时间的占比
-XX:GCTimeRatio=ratio
-XX:MaxGCPauseMillis=ms
-XX:ParallelGCThreads=n
3.响应时间优先(并发)
一般并发标记时,垃圾回收不会影响其他用户线程,所以不需要暂停时间(阻塞,STW)。
-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld
-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads
-XX:CMSInitiatingOccupancyFraction=percent
-XX:+CMSScavengeBeforeRemark
4. G1垃圾回收器(Garbage First)
适用场景:
- 1.同时注重吞吐量和低延迟,默认的暂停目标是 200 ms
- 2.超大堆内存,会将堆划分为多个大小相等的 Region
- 3.整体上是 标记+整理 算法,两个区域之间是 复制 算法
相关 JVM 参数:
-XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time
1、G1垃圾回收阶段
2、Young Collection(新生代收集阶段)
3、Young Collection + CM(并发标记阶段)
- 1.在 Young GC 时会进行 GC Root 的初始标记
- 2.老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW)
- 3.阈值由 JVM 参数决定:
-XX:InitiatingHeapOccupancyPercent=percent(默认为45%)
4、Mixed Collection(混合收集阶段)
会对 E、S、O 进行全面垃圾回收
- 最终标记(Remark)会 STW(StopThe World)
- 拷贝存活(Evacuation)会 STW
- JVM参数:-XX:MaxGCPauseMillis=ms
5、Full GC辨析
- SerialGC(串行)
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc - ParallelGC(并行)
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc - CMS
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足 - G1
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足
1、四种垃圾收集器因为新生代内存不足触发的垃圾收集都称为Minor GC
2、SerialGC和ParallerGC因为老年代内存不足触发的垃圾收集可以直接称为Full GC
这个就是之前讲垃圾收集算法时的那种情况。
3、但是CMS和G1因为老年代内存不足触发的垃圾收集还要分两种情况,以G1为例:
- 1.当老年代内存占整个堆内存的45%时就会触发并发标记阶段和混合收集阶段
- 2.这两个阶段工作的过程中,如果垃圾回收的速度高于新的用户线程产生垃圾的速度这时还不能称为Full GC,此时还处于并发收集阶段
- 3.如果垃圾回收的速度低于新的用户线程产生垃圾的速度,这时并发收集就失败了,会退化为串行收集,此时称为Full GC,收集速度比较慢。
6、Young Collection跨代引用
新生代回收的跨代引用(老年代引用新生代)问题
7、Remark
pre-write barrier + satb_mark_queue
8、 JDK 8u20 字符串去重
- 优点:节省大量内存
- 缺点:略微多占用了 cpu 时间,新生代回收时间略微增加
-XX:+UseStringDeduplication - 将所有新分配的字符串放入一个队列
- 当新生代回收时,G1并发检查是否有字符串重复
- 如果它们值一样,让它们引用同一个 char[]
- 注意,与 String.intern() 不一样
String.intern() 关注的是字符串对象
而字符串去重关注的是 char[]
在 JVM 内部,使用了不同的字符串表
9、JDK 8u40 并发标记类卸载
所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类 -XX:+ClassUnloadingWithConcurrentMark 默认启用
10、JDK 8u60 回收巨型对象
- 一个对象大于 region 的一半时,称之为巨型对象
- G1 不会对巨型对象进行拷贝
- 回收时被优先考虑
- G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0 的巨型对象就可以在新生代垃圾回收时处理掉
11、JDK 9 并发标记起始时间的调整
- 并发标记必须在堆空间占满前完成,否则退化为 FullGC
- JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent
- JDK 9 可以动态调整
-XX:InitiatingHeapOccupancyPercent 用来设置初始值
进行数据采样并动态调整
总会添加一个安全的空档空间