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的表
- card table
- CSet = Collection Set
- 需要被回收的垃圾最多的card都放到这张表格中
- 要回收的时候去CSet中找
- 🌟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的话,调优十几个参数就够了!!!
- G1的Eden区可以占整个堆的5%-60%,是G1自己动态调整的,主要根据设定的响应时间来进行调整
- ❓G1中的GC分为两种
- YGC,G1会根据响应时间动态的调整伊甸区,多线程并行执行回收young区(copy算法),新老都是用G1的同一算法(银弹)
- 当堆总的大小达到一个阈值之后就进行MixedGC,MixedGC是什么意思?就是和CMS差不多,只是回收老年代吗???那这时的年轻代怎么办???还是回收全部的堆内存???
- MixedGC与CMS最大的不同就是最终的筛选回收阶段是STW的,而CMS是并行清理,不需要筛选(筛选的是region)
- ❓G1的FullGC和MixedGC分别是什么?有什么区别?在进行MixedGC之前是用的什么GC???
- 基本概念:
CMS日志
六个阶段
CMS暂停、卡顿的时长如何控制—->可以有参数进行控制的
- 就算设定好了,程序也只能说是尽量按你这么执行,而不是完全依照你设定的那么执行(Hotspot只会尽量朝这个方向去努力)(0ms无论如何也完成不了,只是尽量朝那方面去努力而已)
CMS不行了,老奶奶Serial Old就得上场
- CMS的两大问题:
- 碎片化
- 浮动垃圾
- 浮动垃圾比较好解决,碎片化的问题很严重
- 因为CMS用的是Mark-Sweep算法,所以不会进行压缩整理,所以会产生很多碎片,会造成Promotion Failure
- 这时候就用Serial Old进行清理-整理(压缩),让他用一根线程在这里面做标记-压缩
- 设计CMS就是为了减少响应时间,可惜碎片化
- 当产生了浮动垃圾在下一次CMS之前进行GC之前,新生代又有很多对象进入到老年代了,这时老年代因为有浮动垃圾的存在而放不下这些对象,这时会把所有的线程都停住(STW),使用Serial Old进行清理
- 上图中圈起来的解决方案降低了触发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
- 学完原理学参数