垃圾回收器

垃圾回收器可以分为三类:串行的垃圾回收器、吞吐量优先的垃圾回收器和响应时间优先的垃圾回收器。

串行的垃圾回收器,底层是一个单线程的垃圾回收器,也就是说在进行垃圾回收时,其他的线程会暂停。使用场景是堆内存较小的时候,适合个人电脑(CPU个数少)。而吞吐量优先的垃圾回收器和响应时间优先的垃圾回收器,则是一个多线程的垃圾回收器,适合堆内存较大,需要多核CPU来支持。显然多核CPU都是服务器,即多用在服务器上。

响应时间优先注重的是垃圾回收时STW的单次时间尽可能短,它要考虑的是尽可能让暂停的时间变短。而吞吐量优先的目标是单位时间内STW的时间最短。

串行

开启串行垃圾回收器的语句是:-XX:+UseSerialGC=Serial+SerialOld。

串行垃圾回收器很明显分为两个部分:Serial和SerialOld。其中Serial是运行在新生代中,采用的算法是复制算法;而SerialOld是工作在老年代,采用的算法是标记加整理,不会产生碎片但是效率比较低。新生代和老年代的垃圾回收器是分开运行的。

假设现在有多核CPU,如上图所示,总共有四个CPU,刚开始的时候这些线程都在运行,这时候发现堆内存不够了,随即触发了一次垃圾回收。触发垃圾回收首先需要要这些线程在一个安全点停下来,因为在垃圾回收的过程中,对象的地址要发生一个改变,为了保证安全的使用这些对象地址,需要所有用户工作的线程到达这个安全点暂停下来,然后就可以完成垃圾回收的工作,就不会有其他线程来干扰。因为Serial和SerialOld都是单线程的垃圾回收器,因此只有一个垃圾回收线程在运行,在这个垃圾回收线程运行时,其他线程都要处于阻塞状态,直到垃圾回收线程的结束才可恢复运行。

吞吐量优先

要使用吞吐量优先的垃圾回收器的命令:-XX:+UseParallelGC(工作于新生代,采用的复制算法)~-XX:+UseParallelOldGc(工作于老年代,采用标记加整理的算法)。不过在JDK1.8中是默认开启的。只要开启其中的一个,会自动开启另外一个

吞吐量优先采用的算法和串行垃圾回收器采用的算法都是一样的,都不会产生碎片,只是吞吐量的是多线程的。

它的工作模式是:多核CPU下,多个线程都在运行,这个时候内存不足,触发了一次垃圾回收,用户线程都会在一个安全点停下来,之后垃圾回收器会开启多个线程来进行垃圾回收。默认情况下,垃圾回收的线程数是和CPU的核数相关的,只要你的CPU核数是小于一个值的时候,就跟CPU的核数是一模一样的,比如现在四个CPU,那么就有四个线程进行垃圾回收,当垃圾回收之后就恢复其他线程的工作。在进行垃圾回收的时候CPU的使用是非常高的。

当然垃圾回收线程数可以通过指令来设置:-XX:ParallelGCThreads=n,后面跟上一个线程数。与之相关的还有三个参数:

-XX:+UseAdaptiveSizePolicy——采用自适应的大小调整策略,主要是调整新生代的大小,之前了解过新生代分为伊甸园和两个幸存区,如果把这个开关打开,在ParllelGC时会动态的去调整伊甸园和幸存区的一个比例,包括整个堆的一个大小。

-XX:GCTimeRatio=ration——主要是调整吞吐量的目标,调整垃圾回收时间和总时间的占比。在这里有一个公式:1/(1+ration),ration的默认值是99,即垃圾回收的时间不能超过整个时间的百分之一,如果不能达到这个目标,ParallelGC就会尝试调整堆的大小从而来达到这个目标,一般是把堆增大(因为增大堆空间就让垃圾回收变得不频繁),一般设置为19

-XX:MaxGCPauseMillis=ms——最大暂停毫秒数,默认值为200ms和上面一个命令是冲突的。要跟实际情况去一个合理的值。

响应时间优先

开启响应时间垃圾回收使用命令:-XX:+UseConcMarkSweepGc(基于标记清除算法,并且为并发的,工作在老年代的);与之相配对的是-XX:+UseParNewGC(工作在新生代,采用基于复制算法的垃圾回收),两个一起运行。当并发失败时,这时候会让老年代的垃圾回收器从CMS垃圾回收器回退到SerialOld单线程垃圾回收器

所谓的并发是指垃圾回收器在工作的同时,其他的用户线程也能同时进行,也就是用户线程和垃圾回收线程是并发执行,会同时抢占CPU,而吞吐量优先中的并行是指多个垃圾回收器它们并行运行,在这期间不允许用户线程工作运行。并发进一步减少了STW的时间。

工作流程:多个CPU并行执行,老年代发生内存不足,这些线程会在安全点暂停下来,此时CMS垃圾回收器会执行一个初始标记的操作,在初始标记的过程中仍然需要STW,也就是其他线程会暂停下来,初始标记的时间是非常短的,因为它只会标记堆内存中的根对象;当初始标记完成时,接下来用户线程就可以恢复运行,与此同时,垃圾回收线程可以并发标记把那些剩余的垃圾找出来,在这个过程中不用暂停其他线程时间;等到并发标记以后,还要做一步重新标记,这一步又要STW,因为在并发标记的同时,用户线程也在同时工作就有可能产生新的对象,改变现有对象的引用,对垃圾回收做一些干扰,因此需要重新标记。等重新标记完了以后,用户线程就可以恢复运行,垃圾回收线程并发执行清理操作。

在初始标记时并发线程数会受到-XX:ParallelGCThreads=n和-XX:ConcGCThreads=n两个的影响。前者是并行线程数,后者并发线程数,一般后者设置为CPU核数的四分之一。CMS垃圾回收器虽然做到了响应时间优先,但是由于它占用了一定的CPU使用量,因此对于整个应用的吞吐量是有所影响的。

-XX:CMSInitiatingOccupancyFraction=precent用于设置执行CMS垃圾回收的内存占比,用于预留一部分空间给那些浮动垃圾来用,所谓的浮动垃圾是指在并发清理时,用户线程可能会产生新的对象以及新的垃圾,而这些垃圾只有在下一次垃圾回收时才能被清理。在早期的JVM里面这个默认值为65%。

-XX:+CMSScavengeBeforeRemark——在重新标记的环节,有一个特殊的场景,有可能新生代的对象会引用老年代的对象,这时我在重新标记时必须扫描整个堆,通过新生代引用扫描一遍老年代的对象,做一次可达性分析,这样的话对我的性能影响会非常大,因为在找老年代的过程中会扫描新生代中的一些垃圾,即做了一些重复工作,此时就可以用这个参数,在重新标记之前先对新生代做一次垃圾回收,这样的话就可以减轻重新标记的压力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑白键的约定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值