zoukankan      html  css  js  c++  java
  • 深入理解java虚拟机之自动内存管理机制(三)

    各类垃圾收集器与GC日志

    (一)垃圾收集器

      一、Serial收集器

        最基本、历史最悠久的收集器。使用复制算法,用在新生代,通常老年代用Serial old配合。GC过程需要stop the world。适用于client模式下的虚拟机。

      二、ParNew收集器

        Serial多线程版本,采用复制算法时,开启多线程进行复制。能与Serial old配合,且是唯一能与cms收集器配合使用的新生代收集器。适用于server模式下的虚拟机。在多cpu的环境
      下效果更好。

      三、Parallel Scavenge收集器

        关注点是达到一个可控制的吞吐量,使用复制算法。吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。

      四、Serial Old收集器

        Serial收集器的老年代版本,单线程收集器,使用标记-整理算法,适用于Client模式下的虚拟机。

      五、Parallel Old收集器

        Parallel Scavenge老年代版本,使用标记-整理算法。

      六、CMS收集器

        以获得最短回收停顿时间为目标的收集器。运作过程分为4个步骤。

        1. 初始标记。该阶段就是标记一下GC Roots能直接关联的对象。

        2. 并发标记。就是GC Roots tracing,该过程可与用户线程并发执行。

        3. 重新标记。修正并发标记期间发生变动的对象的标记。

        4. 并发清除。该过程可与用户线程并发执行。

        缺点:

        1. 对cpu资源敏感。如果不是多核,则由于存在两个并发过程,会导致用户程序执行缓慢。

        2. 无法处理浮动垃圾,并且由于在清除阶段与用户线程并发执行,可能由于内存不足而导致一次full gc的产生。所谓浮动垃圾,就是在重新标记过后产生的垃圾。解决办法是预留足够内存。

        3. 由于采用标记-清除算法,所以容易产生大量内存碎片。

      七、G1收集器

        G1是革命性的。它有以下几个特点。

        1. 并行与并发。G1可以让本来需要stw的动作通过并发的方式让java程序继续执行。

        2. 分代收集。G1收集器自身就可以管理新生代和老年代的对象。

        3. 空间整合。整体使用标记-整理算法,局部使用复制算法。

        4. 可预测的停顿。G1会建立可预测的停顿时间模型。

        G1与传统收集器不同的地方:

        G1将java堆分为许多个大小相同的独立区域(Region),新生代和老年代之间不再是物理隔离的,它们都是一部分Region的集合。

        G1为什么能建立可预测的停顿时间模型?

          G1可以有计划地避免在Java堆的进行全区域的垃圾收集,通过跟踪各个Region获得其收集价值大小(回收所获得的空间大小以及回收所需要的时间的经验值),在后台维护一个优先列表

        每次根据允许的收集时间,优先回收价值最大的Region(名称Garbage-First的由来),这就保证了在有限的时间内可以获取尽可能高的收集效率。

        G1如何解决一个对象被不同区域引用的问题?

          不止G1要解决这个问题,其他收集器也要解决这个问题,其他收集器需要解决新生代与老年代之间的对象引用问题。都是通过使用Rememed Set来避免全堆扫描。G1的每个Region都有一

        个与之对应的Remembered Set,虚拟机发现程序在对引用类型的数据进行读写操作时,会产生一个Write Barrier暂时中断写操作,检查引用类型引用的对象是否处于不同的Region之中,如果是,就

        通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在Gc根节点枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

        关于OopMap 和 Remembered Set

          总的来说:OopMap用于枚举根节点,Remembered Set 用来作可达性分析。

          OopMap避免了全栈扫描,Remembered Set避免了全堆扫描。

          新生代 GC(发生得非常频繁)。一般来说, GC过程是这样的:首先枚举根节点。根节点有可能在新生代中,也有可能在老年代中。这里由于我们只想收集新生代(换句话说,不想收集老年代),

        所以没有必要对位于老年代的 GC Roots 做全面的可达性分析。但问题是,确实可能存在位于老年代的某个 GC Root,它引用了新生代的某个对象,这个对象你是不能清除的。那怎么办呢?

          与OopMap一样,用空间换时间,用一个数据结构保存这种引用信息,这样在只需要再新生代上利用这两个东西就能完成可达性的分析。RememberedSet记录的是新生代的对象被老年代引用的关系。

        所以“新生代的 GC Roots ” + “ RememberedSet 存储的内容”,才是新生代收集时真正的 GC Roots 。

        参考:https://dsxwjhf.iteye.com/blog/2201685

                        https://blog.csdn.net/ifleetingtime/article/details/78934379

        

        G1的运作过程(不计维护Remembered Set)

        1. 初始标记(Initial Marking)           

          仅标记一下GC Roots能直接关联到的对象;

             且修改TAMS(Next Top at Mark Start),让下一阶段并发运行时,用户程序能在正确可用的Region中创建新对象;
             需要"Stop The World",但速度很快;


        2. 并发标记(Concurrent Marking)
           进行GC Roots Tracing
           耗时较长,但与用户线程并发执行;
      
        3. 最终标记(Final Marking)
           为了修正并发标记期间变动的对象的标记记录
           上一阶段对象的变化记录在线程的Remembered Set Log;
           这里把Remembered Set Log合并到Remembered Set中;
             需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短;
        采用多线程并行执行来提升效率;
             
      4. 筛选回收(Live Data Counting and Evacuation)
              首先排序各个Region的回收价值和成本;
              然后根据用户期望的GC停顿时间来制定回收计划;
              最后按计划回收一些价值高的Region中垃圾对象;
              回收时采用"复制"算法,从一个或多个Region复制存活对象到堆上的另一个空的Region,并且在此过程中压缩和释放内存;

    (二)GC日志

          https://blog.csdn.net/xiaodu93/article/details/61926114

     

  • 相关阅读:
    nginx:安装成windows服务
    org.aspectj.apache.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 18
    数据库中间件
    架构策略
    谈判
    设计模式 总结 常用10种
    08 状态模式 state
    07 策略模式 strategy
    06 命令模式(不用)
    05 观察者模式 Observer
  • 原文地址:https://www.cnblogs.com/one-code/p/10079911.html
Copyright © 2011-2022 走看看