我们提供安全,免费的手游软件下载!
以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。
并行收集: 指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。
并发收集: 指用户线程与垃圾收集线程同时工作(不一定是并行的可能会交替执行)。用户程序在继续运行,而垃圾收集程序运行在另一个CPU上
吞吐量: 即CPU用于运行用户代码的时间与CPU总消耗时间的比值(吞吐量 = 运行用户代码时间 / ( 运行用户代码时间 + 垃圾收集时间 )),也就是。例如:虚拟机共运行100分钟,垃圾收集器花掉1分钟,那么吞吐量就是99%
特点:单线程、简单高效(与其他收集器的单线程相比),采用 复制算法 。对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。收集器进行垃圾回收时,必须暂停其他所有的工作线程,直到它结束(Stop The World)参数:-XX:+UseSerialGC -XX:+UseSerialOldGC
安全点: 让其他线程都在这个点停下来,以免垃圾回收时移动对象地址,使得其他线程找不到被移动的对象因为是串行的,所以只有一个垃圾回收线程。且在该线程执行回收工作时,其他线程进入阻塞状态
Serial Old 是Serial收集器的老年代版本:采用 标记整理 算法
特点:
单线程收集器
收集效率高,不会产生对象引用变更
STW时间长
使用场景:适合内存小几十兆以内,比较适合简单的服务或者单CPU服务,避免了线程交互的开销。
优点:小堆内存且单核CPU执行效率高。
缺点:堆内存大,多核CPU不适合,回收时长非常长。
年轻代:-XX:+UserParNewGC 老年代搭配 CMS
ParNew收集器其实就是Serial收集器的多线程版本
老年代:-XX:+UserConcMarkSweepGC年轻代搭配ParNew
Concurrent Mark Sweep,一种以获取最短回收停顿时间为目标的老年代收集器
运行过程分分为下列4步:
例如:Math math = new Math();此时new Math()即为math的直接引用对象,再往下为间接引用不做记录,例如构造方法中引用了其他成员变量
在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。
应用场景: 适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如web程序、b/s服务
优点:
并发收集;
STW时间相对短,低停顿;
缺点:
三色标记
黑色:代表了自己已经被扫描完毕,并且自己的引用对象也已经确定完毕。
灰色:代表自己已经被扫描完毕了, 但是自己的引用还没标记完。
白色:则代表还没有被扫描过。标记过程结束后,所有未被标记的对象都是不可达的,可以被回收。
三色标记算法的 问题场景 :当业务线程做了对象引用变更,会发生B对象不会被扫描,当成垃圾回收。
public class Demo3 {
public static void main(String[] args) {
R r = new R();
r.a = new A();
B b = new B();
// GCroot遍历R, R为黑色, R下面的a引用链还未扫完置灰灰色,R.b无引用, 切换时间分片
r.a.b = b;
// 业务线程发生了引用改变, 原本r.a.b的引用置为null
r.a.b = null;
// GC线程回来继续上次扫描,发现r.a.b无引用,则认为b对象无任何引用清除
r.b = b;
// GC 回收了b, 业务线程无法使用b
}
}
class R {
A a;
B b;
}
class A {
B b;
}
class B {
}
当GC线程标记A时,CPU时间片切换,业务线程进行了对象引用改变,这时候时间片回到了GC线程,继续扫描对象A, 发现A没有任何引用,则会将A赋值黑色扫描完毕,这样B则不会被扫描,会标记B是垃圾, 在清理阶段将B回收掉,错误的回收正常的对象,发生业务异常。
CMS基于这种错误标记的解决方案是采取 写屏障 + 增量更新Incremental Update , 在业务线程发生对象变化时,重新将R标识为灰色,重新扫描一遍,Incremental Update 在特殊场景下还是会产生漏标。即当黑色对象被新增一个白色对象的引用的时候,记录下发生引用变更的黑色对象,并将它重新改变为灰色对象,重新标记。
public class Demo3 {
public static void main(String[] args) {
// Incremental Update还会产生的问题
R r = new R();
A a = new A();
A b = new A();
r.a1 = a;
// GC线程切换, r扫完a1, 但是没有扫完a2, 还是灰色
r.a2 = b;
// 业务线程发生引用切换, r置灰灰色(本身灰色)
r.a1 = b;
// GC线程继续扫完a2, R为黑色, b对象又漏了~
}
}
class R {
A a1;
A a2;
}
class A {
}
当GC 1线程正在标记O, 已经标记完O的属性 O.1, 准备标记O.2时,业务线程把属性O,1 = B,这时候将O对象再次标记成灰色, GC 1线程切回,将O.2线程标记完成,这时候认为O已经全部标记完成,O标记为黑色, B对象产生了漏标, CMS针对Incremental Update产生的问题,只能在remark阶段,暂停所有线程,将这些发生过引用改变过的,重新扫描一遍。
多线程
堆内存较大,多核CPU
单位时间内,STW(stop the world,停掉其他所有工作线程)时间最短
JDK1.8默认使用的垃圾回收器
新生代收集器,基于 复制算法 实现的收集器。特点是吞吐量优先,故也称为吞吐量优先收集器,能够并行收集的多线程收集器,允许多个垃圾回收线程同时运行,降低垃圾收集时间,提高吞吐量。 Parallel Scavenge 收集器关注点是吞吐量,高效率的利用 CPU 资源。 CMS 垃圾收集器关注点更多的是用户线程的停顿时间。
Parallel Scavenge 收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的
-XX:MaxGCPauseMillis 参数以及直接设置吞吐量大小的-XX:GCTimeRatio 参数。
-XX:MaxGCPauseMillis 参数的值是一个大于0的毫秒数,收集器将尽量保证内存回收花费的时间不超过用户设定值。
-XX:GCTimeRatio 参数的值大于0小于100,即垃圾收集时间占总时间的比率,相当于吞吐量的倒数。
该收集器的目标是达到一个可控制的吞吐量。还有一个值得关注的点是: GC自适应调节策略 (与ParNew收集器最重要的一个区别)
GC自适应调节策略: Parallel Scavenge收集器可设置-XX:+UseAdptiveSizePolicy参数。当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动态设置这些参数以提供最优的停顿时间和最高的吞吐量,这种调节方式称为GC的自适应调节策略。
使用场景:适用于内存在几个G之间,适用于后台计算服务或者不需要太多交互的服务,保证吞吐量的服务。
优点:可控吞吐量、保证吞吐量,并行收集。
缺点:回收期间STW,随着堆内存增大,回收暂停时间增大。
是Parallel Scavenge收集器的老年代版本
特点:多线程,采用 标记整理算法 (老年代没有幸存区)
响应时间优先
多线程
堆内存较大,多核CPU
尽可能让单次STW时间变短(尽量不影响其他线程运行)
Java面试题专栏 已上线,欢迎访问。
那么可以私信我,我会尽我所能帮助你。
热门资讯