CMS复习

G1

  • 与PS相比吞吐量降低了10%-15%.但清理垃圾时的停顿时间只有200ms
  • 追求吞吐量用PS,追求程序必须200ms内响应用G1
  • G1是一种服务端应用使用的垃圾收集器,目标是用在多核、大内存的机器上,它在大多数情况下可以实现指定的GC暂停时间,同时还能保持较高的吞吐量
  • 首先回收垃圾最多的那些region(区域)—->Garbage First的由来
  • 之前的内存都是在一块的,无论怎么改变都改变不了内存变大时间变慢的事实,所以就需要改变内存布局(打拳王泰森不行,拿手枪)
  • 重要思想:分而治之
    • 不是再使用超级大的一块,而是把内存分成一块一块的region了
    • 一小块一小块的内存区域从1M、2M、4M到32M
    • 学完原理学参数
  • 每一块region在逻辑上仍然属于某一个分代
  • 有四个分代
    • Old区
    • Survivor区
    • Eden区
    • Humongous区(大对象区):存放大对象的,对象特别大,有可能会跨两个或三个连续的region
  • G1的特点
    • 并发收集
    • 压缩空闲空间不会延长GC的暂停时间;
    • 更易预测的GC暂停时间;
    • 适用不需要实现很高的吞吐量的场景
  • 三色标记:对象有三种颜色分别代表是否被垃圾回收器标记过、还是被标记了一半、还是完全未被标记
  • 颜色指针:一个指针在内存中未经过压缩时是64位,在64bit中拿出来3个bit来其中做一些标记,表示这个指针是不是变过了(原来指向这个对象,后来指向那个对象),垃圾回收时会扫描变化过的指针
  • 吞吐量也很重要怎么办—->升硬件,硬件没那么贵了?

    • 每个分区都可能是年轻代也可能是老年代,但是在同一时刻只能属于某个代。
    • 年轻代、幸存区、老年代这些概念还存在,成为逻辑上的概念,这样方便复用之前分代框架的逻辑。在物理上不需要连续,则带来了额外的好处有 的分区内垃圾对象特别多,有的分区内垃圾对象很少,G1会优先回收垃圾对象特别多的分区,这样可以花费较少的时间来回收这些分区的垃圾,这也就是G1名字的由来,即首先收集垃圾最多分区。
    • 新生代其实并不是适用于这种算法的,依然是在新生代满了的时候,对整个新生代进行回收一整 个新生代中的对象,要么被回收、要么晋升,至于新生代也采取分区机制的原因,则是因为这样跟老年代的策略统一,方便调整代的大小。
    • G1还是一种带压缩的收集器,在回收老年代的分区时,是将存活的对象从一个分区拷贝到另一个可用分区,这个拷贝的过程就实现了局部的压缩。每个分区的大小从1M到,32M不等,但是都是2的冥次方。
  • G1的内存区域不是固定的E或者O

    • 基本概念:
      • card table
        • 卡表
        • 严格来讲,和G1等垃圾回收器关系不大,与是什么具体的垃圾回收器没什么关系,就只是一个实现细节
        • card table主要用于垃圾回收算法中,他的垃圾回收速度比较快
        • 追踪活着的对象的时候并不是很容易
          • 用根可达算法的时候,根对象很有可能到Old区中去了
          • 而在老年代中又指向了年轻代中的对象
          • 这时确定年轻代活着的对象的时候还要去遍历整个的老年代
          • 很恐怖、效率很低
        • 就算做一次YGC也要去扫描整个Old区,这样效率就非常低,效率低得不能再低了
        • 所以JVM内部就把内存分成一个一个的区域,每个区域叫做card
          • 与操作系统中的分页差不多,OS中将内存分成一个page一个page上似的
        • young和old区都被分为一个一个的card,具体的对象存在于一个一个不同的card中
        • 如果老年代中有一个card中的对象指回到了年轻代中的一个对象(不管是哪个对象),就会把这个card标记为Dirty(脏的),说明这个card中有一个对象指回到了年轻代,Old区中那么多card通过位图bitmap来代表哪个是脏的card,0表示不脏,1表示脏的
        • 下次扫描Old区时就只需要扫描Dirty Card
        • 只要Old区中的card中有一个对象指向Y区了,就把他标记为Dirty;JVM内部会跟踪这个引用,这个引用从Old区指向Y区就标记
        • 上面这个位图bitmap就叫做card table
        • card table是所有card的总表,记录card的表

    image.png

    • CSet = Collection Set
      • 需要被回收的垃圾最多的card都放到这张表格中
      • 要回收的时候去CSet中找

    image.png

    • 🌟RSet = Remmember Set—->CMS(Concurrent Mark and Sweep 并发-标记-清除)
      • 在每一个region里面都一个表格(这块区域本质上是HashSet)
      • 记录着其他Region中的对象到本regiond的引用,其他region中的对象指向了这个region中的对象,这时就将其他region中的引用记在RSet中
      • 这也是帮助垃圾回收使用的
      • 价值在于:使得垃圾回收器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可
      • 记在表格中,就不需要到处去找了,到处看看有没有引用了当前region中的对象,就得整个扫描一遍(高效回收的关键,三色算法实现的一个关键
      • 如果在一个region中的某一个对象在RSet中没有被引用着,就直接把这个对象GC掉
      • 是一个直接引用,详细到对象级别
      • 使用了RSet,占用的空间就更大了,浪费的空间更多了,空间利用率更低了
      • 虽然大不了多少,但是每个region都要记,总的占用的空间也挺大的,总共占用10%左右
      • 到了ZGC就不再有RSet这块区域了,是用了更好的算法做了更进一步的改进—->使用了颜色指针,这些信息记在了从一个region指向另一个region中对象的引用本身里面了—->将引用里面的3位拿出来就记住了
      • 所以ZGC中RSet就没有了,甚至连伊甸区、幸存区、老年代都没有了
      • 使用ZGC的话,调优十几个参数就够了!!!

    image.png

    • G1的Eden区可以占整个堆的5%-60%,是G1自己动态调整的,主要根据设定的响应时间来进行调整
    • ❓G1中的GC分为两种
      • YGC,G1会根据响应时间动态的调整伊甸区,多线程并行执行回收young区(copy算法),新老都是用G1的同一算法(银弹)
      • 当堆总的大小达到一个阈值之后就进行MixedGC,MixedGC是什么意思?就是和CMS差不多,只是回收老年代吗???那这时的年轻代怎么办???还是回收全部的堆内存???
      • MixedGC与CMS最大的不同就是最终的筛选回收阶段是STW的,而CMS是并行清理,不需要筛选(筛选的是region)

    image.png

    • ❓G1的FullGC和MixedGC分别是什么?有什么区别?在进行MixedGC之前是用的什么GC???

CMS日志

  • 六个阶段

    • 初始标记
    • 并发标记
    • 预清理preclean(预标记:将某些card标记为dirty)
    • 重新标记(rescan重新扫描、弱引用处理、类卸载、常量池清理、重新标记)
    • 并发清理
    • 重置内部(数据)结构reset(以便下一次GC,为下一次GC做准备

      JVM(CMS)调优的两大方面:1. 回收得是否频繁 2. 回收的时间是不是很长(允许范围内)

  • CMS暂停、卡顿的时长如何控制—->可以有参数进行控制的

  • 就算设定好了,程序也只能说是尽量按你这么执行,而不是完全依照你设定的那么执行(Hotspot只会尽量朝这个方向去努力)(0ms无论如何也完成不了,只是尽量朝那方面去努力而已)

CMS不行了,老奶奶Serial Old就得上场

  • CMS的两大问题:
    • 碎片化
    • 浮动垃圾
  • 浮动垃圾比较好解决,碎片化的问题很严重
  • 因为CMS用的是Mark-Sweep算法,所以不会进行压缩整理,所以会产生很多碎片,会造成Promotion Failure
  • 这时候就用Serial Old进行清理-整理(压缩),让他用一根线程在这里面做标记-压缩
  • 设计CMS就是为了减少响应时间,可惜碎片化
  • 当产生了浮动垃圾在下一次CMS之前进行GC之前,新生代又有很多对象进入到老年代了,这时老年代因为有浮动垃圾的存在而放不下这些对象,这时会把所有的线程都停住(STW),使用Serial Old进行清理

image.png

  • 上图中圈起来的解决方案降低了触发CMS的阈值,这样也就降低了Promotion Failure和ConcurrentModeFailure发生的可能性,从而最大可能地避免了使用Serial Old STW进行清理而造成巨大停顿时间的可能性,在使用Serial Old之后他还是会发生OOM的
  • G1中只有逻辑分代,他的里面是是一个一个的region,在进行垃圾回收的时候会从一个region copy到另一个region中去进行压缩,所以基本没有上面的那个问题


CMS中的FGC

question:CMS的时候老年代中满了或者达到阈值的时候是会进行FGC吗?这里的FGC是否是在对整个堆内存的GC过程中,让年轻代用ParNew进行回收,而让老年代用CMS进行回收? answer:对的 .只有CMS的concurrent collection是这个模式—-只收集老年代,也就是说 其他的老年代收集器,都会使用老年代的gc算法时对整堆收集,而不是只针对老年代

G1日志

  • G1的YGC是STW的,G1中有STW
  • G1根据YGC的暂停时间调整年轻代的大小(分区越大,回收的时间越长,因为遍历的时间越长呀)
  • G1有三个阶段(不是顺序执行的,而是在一定条件下会触发)
    • YGC
    • MixedGC
    • FGC
  • G1也有FGC,所以G1的调优目标就是不要让他FGC
  • G1与CMS最后让单线程在那扫地是一样的????(老奶奶打扫小区)
  • G1在很多参数和原理上和CMS是很类似的
  • pause一定是STW的
  • Evacuation是复制存活对象的意思
  • 有些年轻代是没有initial-mark的—->是后面的MixedGC的一部分
  • 其实现在YGC已经和其他区的回收混在一起了,其他区的初始标记阶段已经开始了
  • 有可能年轻代回收和MixedGC同时进行,MixedGC伴随着年轻代回收同时进行
  • 每次回收从18.8回收到18.8就是不正常;从18.8回收到8.8就是正常的—->第一种情况可能是产生内存泄漏了!!!~

    G1中垃圾回收的全过程

常用参数

  • 不知道当前的垃圾回收器的时候
    • jinfo
    • arthas
    • 还可以通过看日志发现回收器的类型
  • PrintCommandLineFlags(在windows上有用,在linux上没用)
  • PrintVMOptions(打印jvm运行时的参数!!!)
  • PrintFlagsFinal(所有的参数,你中间可能给他设置了一些值,即多设置的值)或者PrintFlagsInitial(初始化默认的参数,即不加任何参数,他所给的默认参数) + grep
  • MaxTenuringThreshold表示升代年龄(最大值是15),CMS默认的是6,其他的默认是15
  • 锁自旋次数:锁升级的概念中自旋锁自旋多少次会升级成重量级锁(不建议动)
  • 热点代码检测参数:CompileThreshold表示代码执行多少次就成为热点代码,就进行编译JIT
  • 执行方式:纯编译、纯解释、混合模式(默认)

    GC常用参数

    CMS常用参数

软件架构中的两大思想:分而治之+分层

  • 分层
    • TCP/IP
    • 平时应用程序的模型(MVC)(表现层、存储层之类的)
  • 分而治之
    • HBase也有region server,管理着一个一个的分区
    • 很大流量到来的时候,解决方案就是将大量流量分而治之
    • kafka也是
    • G1使用的就是分而治之
      • 不是再使用超级大的一块,而是把内存分成一块一块的region了
      • 一小块一小块的内存区域从1M、2M、4M到32M
      • 学完原理学参数