主要解答

  • JVM垃圾回收算法以及各自优劣势
  • CMS垃圾回收器怎么工作的
  • 服务卡顿元凶
    主流的服务器中常用的垃圾回收器有CMSG1

GC过程和垃圾回收是一个相反的过程,它找到活跃的对象,将剩余内容判定为垃圾,然后回收

标记


根据GC Roots遍历所有的可达对象,这个过程叫做标记。如上图,绿色表示GC Roots,红色表示可达对象。剩余灰色即被回收的对象。

清除

复制

提供一个对等的空间,把存活的对象复制过去,然后清除原内存空间。

整理

类似数组,只需要按内存地址移动存活对象

理想算法模拟过程

last = 0
for(i=0;i<mems.length;i++){
  if(mems[i] != null){
      mems[last++] = mems[i]
      changeReference(mems[last])
  }
}
clear(mems,last,mems.length)

分代

  • 复制算法
  • 标记-清除
  • 标记-整理

研究表明,大部分对象,可以分为两类

  • 大部分生命周期很短
  • 其他对象会存活很长时间

大部分死得快,其他活的长。这个假设称为弱代假设
大部分对象朝生夕灭

现在的回收器,物理上和逻辑上将死的快的对象所占的区域称为年轻代。其他活的久的对象占的区域称为老年代。

年轻代

使用的是复制算法--因为年轻代只有非常少的对象存活,复制起来很快。
年轻代划分

年轻代分为:Eden(伊甸园)、2个Survivor(幸存者)。
当Eden分配满了后,触发年轻代GC(Minor GC):

  • Eden第一次GC,存活对象移动到一个Survivor(简称from)
  • Eden再次GC,采用复制算法,将Eden和from一起清理,存活对象移动到to区;接下来清空from。
    总会有一个survivor是空的,Eden、from、to默认比例是8:1:1,由参数-XX:SurvivorRatio配置(默认为8)

TLAB

全称Thread Local Allocation Buffer,JVM默认给每个线程开辟一个buffer区域,加速对象分配,这个buffer就放在Eden区域。
对象较大时,就会在Enen共享区域分配

老年代

使用标记-清除,标记-整理,老年代中对象存活率高,空间大,copy不划算,不如就地收集。
对象进入老年代的途径:

  • 提升:每次发生Minor GC,存活下来的对象,年龄加1.达到阈值,提升到老年代。由参数-XX:+MaxTenuring Threshold配置,最大15,用4bit存储。
  • 分配担保:survivor默认比例是10%,但是无法保证每次都够用,这是对象会在老年代分配
  • 大对象直接在老年代分配:由参数-XX:PretenureSizeThreshold设置,默认为0,意思是全部首选Eden分配
  • 动态对象年龄判定:和提升(必须达到15)不一样,使用动态算法(比如:幸存区中相同年龄对象大小和大于幸存区一般大于这个age的对象进入老年代)
    一个对象的分配逻辑

卡片标记

老年代被分成卡页(card page,一般数量是2的次幂)。卡表(Card Table)用来标记卡页状态的集合,一对一。
如果年轻代有对象分配,而且老年代有对象指向这个新对象,那么这个老年代所对应的的内存的卡页会被标记为dirty,卡表只需要非常小的存储空间就可以保留这些状态。
垃圾回收时,就可以先读卡表,进行快速判断。

HotSpot垃圾回收器

年轻代垃圾回收器

Serial

处理GC的只有一条线程,并且在回收的过程中暂停一切用户线程。高效,用在客户端应用上,因为客户端不会频繁创建对象,用户不会感觉出明显卡顿。

ParNew

Serial多线程版本,多条GC线程并发清理,但是仍要暂停用户线程。单CPU中因为线程切换,所以性能不如Serial。

Parallel Scavenge

另一个多线程垃圾回收器,与ParNew区别为:

  • Parallel Scavenge:追求CPU吞吐量,能在较短时间完成指定任务,适合没有交互的后台计算。弱交互强计算。
  • ParNew:追求降低用户停顿时间,适合交互式应用。强交互弱计算。

老年代垃圾收集器

Serial Old

年轻代的Serial使用复制算法,老年代使用标记-整理算法。

Parallel Old

追求CPU吞吐量。

CMS

CMS(Concurrent Mark Sweep)是以获取最短的GC停顿时间为目标的收集器,用户线程和GC线程并发。
长期以来,CMS是要被G1替换的。Java8之后,使用它会抛出警告
Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.

配置参数

还有G1、ZGC等高级垃圾回收器,有专门的参数配置使之生效。
通过-XX:+PrintCommandLinesFlags参数,查看当前Java版本默认使用的垃圾回收器。
jdk13默认使用G1GC

一些配置参数


Java8默认Parallel Scavenge

STW

STW(stop the world):垃圾回收时,又有新的对象进入,最好办法是暂停用户一切线程,就是STW。即使是最先进的ZGC也会有STW。

STW影响

应用峰值流量10w/s,10台负载均衡。每台机器平均1w/s。某台机器STW1s,那么本来10ms就可以返回1w个请求,需要等待1s,用户就能感觉到卡顿。

总结

垃圾回收就是解决STW问题。

算法

  • Mark
  • Sweep
  • Copy
  • Compact

分代

  • Young generation
  • Survivor
  • Eden
  • Old generation | Tenured Generation
  • GC (Minor GC、Major GC)
Last modification:April 1st, 2020 at 12:42 am