zoukankan      html  css  js  c++  java
  • gc算法

    1 gc算法

      gc的对象是堆空间和永久区的不可触及的对象。

      *对象有三个状态 可触及:从根节点可以触及到这个对象;可复活:引用被释放,但是finalize方法中可复活;不可触及:引用释放,finalize不复活。注意,finalize只会在第一次垃圾回收的时候调用一次,以后不会再掉用,而且调用时间不确定,避免使用finalize防止对象永远存在。

      *根节点:栈中引用的对象,方法区静态成员(全剧对象)可以作为根节点。

      (1)引用计数法  通过引用计数计算可达性来回收垃圾,为每个对象标注使用数量,引用可达则加一,引用失效则减一。从根对象算起,引用可达则为有效对象。缺点:引用和去引用伴随加减,影响性能;解决不了孤岛问题,即循环引用问题。java没有使用引用计数法。

      (2)标记清除 主流垃圾回收的思想基础。可能产生内存碎片,内存空间不连续。

      标记阶段:从根节点标记所有可达对象。

      清除节点:清除所有未被标记或标记为垃圾的对象。

      (3)标记压缩 适用于存活对象较多的场景,如老年代,在标记清除算法的基础上做了优化。比标记清除的优点在可用内存更有序有连续内存。

      以标记清除为基础,把所有表的的存活对象压缩到内存一端,然后清除边界外所有空间。

      (4)复制算法 相对于标记清楚较为高效,不适合存活对象较多的场合如老年代,主要用于新生代。

      将原有内存空间分为相等大小的两块,每次只用其中一块,垃圾回收时将正在使用的内存中的存活对象复制到未使用的内存中,然后清除正在使用的内存中的所有对象。缺点是只能用一半内存,资源浪费。

      实际作用时,JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。在GC开始的时候,对象只会存在于Eden区和From区,To是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到To,而在From区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到To区。经过这次GC后,Eden和From已经被清空。这个时候,From和To会交换他们的角色。GC会一直重复这样的过程,直到To被填满,To被填满之后,会将所有对象移动到年老代中。

      *分代思想  

      新生代多为短命对象,gc有少量对象存活,适合复制算法。

      老年代多为长期对象,gc会有大量对象存活,适合标记清除或标记压缩。

      *stop-the-world   java中一种全局暂停的现象,多半由gc引起,也可能由dump线程、死锁检查、堆dump等引起。gc边清理系统边产生,垃圾清理不完,只有停止系统所有活动才能清理完。这种现象难以避免,新生代gc停顿比较短,老年代gc可能很长。

    2 gc种类

      minor,major/full

      *Minor GC 新生代gc。 当jvm无法给一个新对象分配空间时会出发,即eden满了触发,不会影响到永久代,会触发stop-the-world,由于新生代对象存活率低,所以暂停不会太久。

      *Full GC  老年代或永久代空间不足触发。还有一种情况,为了避免新生代转入老年代导致老年代空间不足,在minor gc时,如果minor gc转入老年代的平均大小大于老年代剩余的内存,那么也会触发。  

    3 gc收集器

      (1)串行收集器,古老稳定高效的收集器,但是停顿时间比较长,因为只使用一个线程进行回收。

         -XX:+UserSerialGC ,新生代使用复制算法,老年代使用标记压缩算法。

      (2)并行收集器 ParNew  只在新生代并行。

         -XX:+UseParNewGC,新生代并行,老年代串行,新生代还是复制算法,老年代标记压缩。多核cpu会比较快,单核建议串行收集器

         -XX:ParallelGCThreads: 线程数量 

      (3)并行收集器 Parallel收集器

            更加关注吞吐量的收集器,新生代还是复制算法,老年代标记压缩.

        -XX:+UseParallelGC  新生代并行,老年代串行。

        -XX:+UseParallelOldGC  新生代老年代都并行。  

      (4)cms收集器  concurrent mark sweep 并发标记清除

        使用标记清除算法,与应用程序线程一起执行,停顿会减少一些,吞吐量也会降低。适用于老年代,新生代还是用parnew。

        -XX:+UseConcMarkSweepGC

        *-XX:MaxGCPauseMills  最大停顿时间,gc尽量不超过,但不保证。

        *-XX:GCTimeRatio 垃圾收集时间跟总时间占比,默认99 即1%时间gc。

        cms运行分为四步,其中有两步是并发两步单线程,尽可能缩小全局停顿时间但不能完全避免,分别为

          *初始标记,从根节点关联到对象,停顿不并发,(CMS-initial-mark)

          *并发标记,在程序运行过程中标记全部对象,和用户线程一起。(CMS-concurrent-mark)

          *重新标记,在正式清理前再做修正,停顿不并发。(CMS-remark)

          *并发清除,基于标记结果直接清理对象,和用户线程一起。(CMS-concurrent-sweep)

          *并发重置,并发重设状态等待下次CMS的触发(CMS-concurrent-reset)

          由此可见,cms会尽可能降低停顿,但清理不彻底,因为是并发的所以应用会一直有垃圾,也是因为并发,所以不能在空间快满时再清理。如果清理速度赶不上应用内存申请速度,会报 concurrent mode failure,此时应该使用串行收集器,暂停应用来回收。

       *照顾到不断并行产生的新对象,cms用的标记清除不是标记压缩。所以会产生内存碎片。可以配置参数

          -XX:+ UseCMSCompactAtFullCollection fullgc后进行一次整理。

          -XX:+ CMSFullGCsBeforeCompaction 设置几次fullgc后进行一次碎片整理

          -XX:ParallelCMSThreads cms线程数量

      4 如何减轻gc压力

         系统架构设计,代码编写,堆空间分配。

  • 相关阅读:
    局部测试用例,日常笔记
    软件测试工程师素养(日常笔记)
    Java控件(日常笔记)
    开发大体流程
    sort学习 LeetCode #406 Queue Reconstruction by Height
    MySQL 变量
    [转帖]查看结构体成员的大小和偏移地址的方法
    [转帖]SQL99
    static静态类 静态函数 静态字段
    默认构造函数
  • 原文地址:https://www.cnblogs.com/lkdirk/p/6368045.html
Copyright © 2011-2022 走看看