JVM STW(Stop The World)到底怎么回事(三)

目录:

  1. 新生代对象从哪里来,什么时候来?
  2. 老年代的对象从哪里来,什么时候来?
  3. 什么时候触发Minor GC?
  4. 什么时候触发Full GC?
  5. STW到底是什么,什么时候触发?
  6. JVM运转整体流程图
  7. JVM面试题,如何做到一个系统几乎0次Full GC

1. 新生代对象从哪里来,什么时候来?

距离咱们开发的后台服务J2EE以及对应的SpringBoot项目等,对应的Controller里面构造的任何对象都是去了哪里呢?

都是由用户通过浏览器,或者移动终端等发送请求至咱们部署的服务端对应的请求地址,一般的Web容器这个时候就会启动这么一个线程去服务好当前用户所发送的请求,需要接收它的参数,以及处理对应的业务到最终返回用户所需要的结果。

那么根据上图可以看到,新的对象都是通过这么服务用户的线程创建过来的,首先肯定是往新生代走,而且是对应的Eden(伊甸园区)。

从哪里来,什么时候来:业务触发的时候,由对应的线程创建而来

2. 老年代的对象从哪里来,什么时候来?

当系统一直在对外服务的时候,新生代的空间已经在不足以容纳新的对象时候,经过一系列漫长的过程他就会往老年代晋升。

老年代的对象来源:

  • 从新生代晋升过来
    • 新生代的对象躲过15次MinorGC(默认)
    • 新生代相同年龄的对象之和超过Survivor综合的50%,则会进入老年代
  • 设置了-XX:PertenureSizeThreshold(大对象直接进入)

3.什么时候触发Minor GC?

触发Minor GC肯定是在系统需要在新生代申请内存空间的时候不足以开辟新的内存空间来存放系统所需要创建的对象大小。

根据上面的这个图示可以看出,假如新生代的内存空间剩余只有10mb了,而现在系统需要申请20mb的内存空间,那么就会走入上面的触发垃圾回收。这个时候中间其实会经过一系列的判断,到底这个垃圾回收该怎么走?那么接下来就要说Full GC了,因为在触发垃圾回收的时候有可能会先进入Full GC,而不是先执行Minor GC。

4.什么时候触发Full GC?

刚刚上面说着Minor GC,为什么会与Full GC有关呢?那到底什么时候回执行Full GC呢?可以看到当需要申请的内存空间不足时,这个时候是需要触发执行垃圾回收的。那么在执行Minor GC执行会出现以下这些场景。

即假如这个时候我们不看老年代,就单纯以年轻代来说。现在年轻代的空间已经不足了,是不是。行那么咱们就执行Minor GC呗,但是万一呢?万一要是执行完Minor GC之后,其中大部分都还是存活的对象怎么办(这个时候一般可能是由于响应时间变大了,导致系统负载越来越差,也就是说这个线程一直没有走完,其内存中的对象引用一直也没有释放,导致这个时候如果执行Minor GC,那么大部分对象都还是存活的)

那我们接着说如果万一执行Minor GC之后存活对象还是无法满足系统所需要新申请的内存空间,这个时候是不是就需要把年轻代里面的对象往老年代晋升了是不是?那在晋升之前,是不是也应该判断老年代的所剩余空间大小也要足以放得下这些新晋升的对象呢?如果放得下,那还好。直接晋升上来就可以了,那如果放不下,那是不是这个时候要就要执行Full GC了,把老年代的内存空间清理清理是不是?如果这个时候老年代Full GC之后还是不够,那么这个时候就是咱们经常看到的OOM了。通过这么个描述下来之后,实际流程跟咱们描述会有一点差异。回过头来再看看触发垃圾回收。

  1. 触发垃圾回收
  2. 首先会判断老年代剩余空间是否大于年轻代所使用的内存大小,大于,则直接进行Minor GC,因为就算你Minor GC后所有的对象全部都存活,那么我老年代也放的下
  3. 如果老年代小于新生代所使用的空间呢?这个时候就要看有没有设置这么一个担保机制了,如果设置-XX:HandlePromotionFailure,那么就要看之前每一次Minor GC后晋升到老年代的平均大小是否大于或小于老年代的剩余空间大小
    1. 平均晋升对象大小小于老年代剩余空间大小,这个时候就先执行Minor GC
    2. 平均晋升对象大小大于老年代剩余空间大小,这个时候就先执行Full GC
  4. 如果没有设置担保机制?那么这个时候就要先进行一次Full GC了,以防Minor GC后导致老年代还是放不下晋升对象大小。
  5. 另外还有一个会执行Full GC时机,在上图中没有画出来。后台会有一个线程定时扫描,即如果老年代的剩余空间超过一个比值,那么这个时候也会执行Full GC。-XX:CMSInitiatingOccupancyFaction,可以通过这个参数设置。

根据上面的这个流程也就能看的出来了,到底是在什么时候回执行Full GC。

5.STW到底是什么,什么时候触发?

上面说了Minor GC、Full GC,那么接着就是STW了,这个STW是“Stop the world”的简称。即整个Java虚拟机应用线程暂停工作。

先来看看咱们做Minor GC和Full GC的时候,是不是需要去标记或者计算对象是否还在被引用,那么想想看,如果在标记或者计算的过程中如果还有新对象产生。换做是你的话,你能够计算或者又什么方式能够处理这个问题吗?显然JVM设计这个垃圾回收的也没办法,所以这个时候就需要STW了,即Stop The World。暂停工作吧,先把垃圾处理完,大家在继续对外服务工作,要不然大家都玩不下去了。

但是Minor GC与Full GC的STW存在一定的差异,Minor GC的STW肯定要比Full GC的STW短暂的多的多,几乎可以忽略不计,那后续我们再来分析到底差异在哪里,为什么会差别这么大。

6.JVM运转整体流程图

下图当中应该缺少动态年龄问题而导致进入到老年代对象的流程,只画了节点,没有画对象从S0、S1进入老年代。

7.JVM面试题,如何做到一个系统几乎0次Full GC

Java后台应用的面试题,需要对这个系统进行JVM调优,让它做到0次Full GC,那到底该怎么弄呢?

根据咱们上面深入的堆这个JVM运转流程分析,也知道在什么时候Minor GC,什么时候Full GC是不是,如果真要做到0次的Full GC是不是就应该尽可能的让对象不要往老年代走,当然除了全局对象以外,比如咱们现在大多数后台开发都是基于Spring是不是,那么当中的Controller、Service、Config、还有Dao等这些单例模式创建的实体肯定是在老年代的。我们说的就是要处理那些基于服务用户只要存活一个线程周期的那些对象。

基于评估这个系统每天的服务请求数、每个请求时长、每个线程所需要创建哪些对象通过一些工具监控出来,合理的分配Eden区,S0、S1区,以及对应的老年代,还有需要考虑注意的就是那些由于动态年龄问题导致而进入到老年代的对象基本上是可以做到0次Full GC的。

Mark:1.7(包含)以后默认开启了担保机制,1.6(包含)之前则需要手动配置担保机制。

分享到 评论