一、Stop The World的烦恼
1、背景
工作线程(例如tomcat线程)一直不停的往堆里面生产对象(生产垃圾)。
gc线程(垃圾回收线程)一直不停的回收垃圾。
这种一边生产垃圾,一边回收垃圾,看似简单,但是对jvm来说,其实是非常复杂的,现阶段的jvm还无法实现一边生产垃圾一边回收垃圾。
所以,在清除垃圾的时候,只能停一边,即停工作线程,让垃圾回收线程全心全意回收垃圾,打扫干净后,再让工作线程干活。
2、什么是安全点
当暂停的这段时间,所有工作线程就进入了安全点SafePoint。
(1)从CPU角度解释:CPU上下文切换,CPU从工作线程切换去执行gc线程。
此时工作线程保存了线程上下文的全部信息,例如程序计数器保存了下一条字节码指令。
(2)从线程的角度解释:线程被挂起,如WAIT、BLOCK状态。只有当gc线程执行结束后,工作线程就继续处于running状态。
3、什么是STW
Java应用程序所有工作线程进入了安全点SafePoint后,所有工作线程被挂起,导致java应用程序全局暂停的现象被称为STW。
STW就是Stop The World,很可怕,整个应用都暂停了。
二、垃圾回收的串行、并行、并发有什么区别
1、串行回收
(单CPU)指垃圾回收线程执行时,用户线程(执行java代码的线程)处于等待状态。
串行回收适合带个CPU或硬件资源较差的服务器,故,串行回收默认应用在客户端的Client模式下使用。
2、并行回收
(多CPU)是多条垃圾回收线程和用户线程分别在不同CPU上同时工作。
不过并行回收仍然与串行回收一样,采用独占式,使用了“stop-the-world”机制。
3、并发回收
垃圾收集器线程和用户线程交替执行,以尽可能减少应用程序的停顿时间。
三、如何判断一个垃圾回收器的好与坏,有哪些性能指标
1、吞吐量(Throughput)
吞吐量:指用户线程执行java代码的时间与CPU总消耗时间的比值,即
吞吐量 = 用户线程执行java代码的时间 / (用户线程执行java代码的时间 + 垃圾收集时间)。
假设虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
2、暂停时间
暂停时间:指应用程序线程暂停,让GC线程执行回收,这段时间称为暂停时间。
例如,GC回收需要100毫秒的暂停时间,那这100毫秒内,你的应用程序就停止100毫秒。
3、吞吐量和暂停时间的关系
例子:
老板生产手机,土豪小王定制一台高端手机(急用),手机工厂如下:
手机生产线,生产线组装单台手机需要总时长6小时,由于是生产线批量操作,每秒能生产1台手机。因此,该生产线每天的吞吐量为86400台,每周的吞吐量为7*86400=604800台。
假设每天要检查维修生产线的设备,需要2小时,这个检查维修就类似JVM的gc回收工作一样。
这2小时对整条生产线的影响如下:
(1)吞吐量影响:每秒1台手机,2小时就是7200台,也就是说7天的吞吐量为(86400 - 7200)/ 86400 = 91.6%,即7天的生产吞吐率为91.6%。
(2)暂停时间:本来一台手机6小时,如果遇到检查维修期间就是6+2=8小时。
对于老板,他关心的是每天每周能生产多少台,即吞吐量,至于生产一台需要多少小时,不是他最关心的。
对于小王,他是vip用户,高端定制用户,而且是急用,本来等6小时就能用,如果正好遇到维修,要多等2小时,小王会很生气。
对于JVM来说,停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
四、剖析年轻代垃圾回收器原理
1、例子
package gc;
public class Book {
private String name;
private String author;
public Book(String name, String author) {
this.name = name;
this.author = author;
}
public void page(long no) {
String page = "page_" + no;
System.out.println(page);
}
}
package gc;
public class Test {
public static void main(String[] args) {
Book book = new Book("jvm", "abc");
long n = 0;
while(true) {
book.page(n);
++n;
}
}
}
2、说明
main方法的栈帧引用了堆的book对象。大量的page方法调用产生了大量的临时对象,即大量ptemp对象,大量的ptemp对象垃圾。(ptemp临时对象就是"page_" + no)
大量的ptemp对象很快就把年轻代占满。
例子只有一条线程运行,但是实际上系统一般是100—200条线程运行,并不停的产生垃圾对象(比如tomcat)
3、年轻代满了,该如何处理呢
答案是:垃圾回收器。为了提升垃圾回收性能,垃圾回收器分为年轻代回收器和老年代回收器。
4、年轻代具体是怎么做垃圾回收的呢
当年轻代满时,会触发gc:
(1)垃圾收集器,先把所有工作线程挂起,所有工作线进入安全点SafePoint后,当前处于STW状态。
(2)gc线程,采用复制算法干了3件事:
第1件事,把eden区的存活对象复制到survivor区
第2件事,survivor的对象年龄>15或survivor满了,复制到old区
第3件事,清空eden区
(3)当gc线程执行结束后,工作线程被唤醒,继续处于running状态。
5、Serial垃圾回收器(第一代垃圾回收器)
以上原理就是Serial垃圾回收器,Serial是1999年发布的,年代最悠久的垃圾收集器,于jdk1.3之前就开始使用。
特点:
(1)分代:年轻代
(2)工作方式:单线程串行回收
(3)算法:复制算法
(4)性能设计:停顿时间短(响应速度快)
应用场景:
单CPU单核的环境下,适用于Client模式下的年轻代收集器。
现在大家只要了解就可以了,现在市面上基本找不到单CPU单核了,所以这个收集器在当今已经过时了。
五、年轻代回收器PairNew + Parallel
1、ParNew垃圾回收器(第二代垃圾回收器)
ParNew它是Serial收集器的多线程版本
特点:
(1)分代:年轻代
(2)工作方式:多线程并行独占式回收
(3)算法:复制算法
(4)性能设计:停顿时间短(响应速度快)
应用场景:
适用于多核多CPU的环境下,并且需要与用户交互的程序(例如,客户端桌面游戏,非常高的响应速度用户体验)的年轻代收集器。
2、ParNew使用参数
-XX:+UseParNewGC:表示年轻代使用了ParNew收集器。
-XX:ParallelGCThreads:表示设置gc线程数量,默认CPU核数。
-XX:MaxGCPauseMillis:表示最大停顿时间(即STW的时间)。单位是毫秒。对用户来说,停顿时间越短体验越好,但使用该参数需谨慎。(默认值为0,代表没有限制停顿时间)
官方建议尽量不要去设置它,原因是为满足最小的暂停时间,VM将设置更小的堆,以存储相对少量的对象,来提升回收速度,会导致更高频率的GC。
3、Parallel垃圾回收器(第三代垃圾回收器)
Parallel它和ParNew回收一样,唯一的区别就是性能设计采用了高吞吐量。
特点:
(1)分代:年轻代
(2)工作方式:多线程并行独占式回收
(3)算法:复制算法
(4)性能设计:高吞吐量
(5)jdk8默认垃圾收集器
应用场景:
高吞吐量适用于多核多CPU的环境下,服务端的高并发(例如,互联网微服务)的年轻代收集器。
没有用户交互的场景,比如定时器。
4、Parallel使用参数
-XX:UseParallelGC:表示年轻代使用了Parallel Scavenge收集器。
5、问题
如果有一个互联网系统,用户天天来访问你这个系统,请问用哪种垃圾回收器?
肯定是ParNew,停顿时间短。
六、老年代Serial & Parallel Old垃圾回收器
1、老年代如何做垃圾回收
1、老年代如何做垃圾回收
(1)工作线程:拼命的产生对象,对象存于eden区。
(2)年轻代垃圾回收器:年轻代满了,触发ygc,把存活对象复制到old区。
(3)老年代垃圾回收器:先把所有工作线程挂起,所有工作线程进入安全点SafePoint后,当前处于STW状态。
年轻代回收有一次STW,老年代回收又有一次STW。
(4)gc线程:
年轻代的回收,它把年轻代的对象复制到老年代,复制完后,再清空对象。
那老年代是否能像年轻代那样呢?
也采用复制算法?没地方复制了,那就只能就地解决了,即采用标记整理算法。先标记后整理。
2、Serial Old老年代垃圾回收器(第一代老年代垃圾回收器)
Serial Old是Serial收集器的老年代版本。
特点:
(1)分代:老年代
(2)工作方式:单线程串行回收
(3)算法:标记整理算法
(4)性能设计:停顿时间短(响应速度快)
应用场景:
单CPU单核的环境下,适用于Client模式下的年轻代收集器。
现在大家只要了解就可以了,现在市面上基本找不到单CPU单核了,所以这个收集器同样在当今已经过时了。
3、Parallel Old老年代垃圾回收器(第二代老年代垃圾回收器)
Parallel Old它是Parallel收集器的老年代版。
特点:
(1)分代:老年代
(2)工作方式:多线程并行独占式回收
(3)算法:标记-整理算法
(4)性能设计:高吞吐量
(4)jdk8默认垃圾收集器
应用场景:
高吞吐量适用于多核多CPU的环境下,服务端的高并发(例如,互联网微服务)的老年代收集器。非用户交互。
4、Parallel Old使用参数
-XX:+UseParallelOldGC,表示老年代为Parallel Old收集器。
-XX:ParallelGCThreads,表示设置gc线程数量,默认CPU核数。