JVM总结_垃圾回收机制

本文是阅读笔记,本人将书中的要点提取出来方便复习、记忆。如须要详细了解,请翻阅《深刻理解Java虚拟机》(周志明)第三章相关部分,谅本人水平有限,描述中有不正确的地方,还请包含与指正。java

  • 概述

    • 问题算法

      • 哪些对象须要回收?
      • 何时回收?
      • 如何回收?
    • 垃圾回收发生的位置api

      • 方法区

    程序计数器、虚拟机栈、本地方发栈是线程私有的,随线程生灭。堆和方法区是线程共享的,这些地方的内存都是动态的分配和回收的。多线程

  • 判断对象须要回收的方法 -- (哪些对象须要回收)

    • 引用计数法( Reference Counting)
      • 描述
        • 当引用失效时,计数器值减1。
        • 当被引用时,计数器值加1。
        • 当计数器为0时,表示该对象没有被引用,能够被回收。
      • 优势
        • 实现简单
        • 效率高
      • 缺点
        • 难以解决对象间相互循环利用的问题
    • 可达性分析算法 ( Reachability Analysis)
      • 描述
        • 从GCRoots起始点开始向下搜索,当一个对象到达GCRoots没有任何引用链相连,即从GCRoots到此对象不可达,则该对象不可用。
      • GCRoots对象
        • 虚拟机栈(栈帧中的本地变量表)中引用的对象
        • 方法区中类静态属性引用的对象
        • 方法区中常量引用的对象
        • 本地方法栈中JNI(Native方法)引用的对象
  • 对象的引用

    • 强引用(Strong Reference)
      • 相似Object obj = new Object(),只要强引用存在,垃圾回收器永远不会回收该对象。
    • 软引用(Soft Reference)
      • 描述一些还有用但非必需的对象。系统将要发生内存溢出以前,将这些对象列入回收范围,进行第二次回收。
      • 若是第二次回收后依然没有足够的内存,将会发生内存溢出异常。
    • 弱引用(Weak Reference)
      • 描述一些非必需的对象。这些对象只能存活在下次垃圾回收以前,不论内存是否充足,下次垃圾回收器都会回收这些对象。
    • 虚引用(Phantom Reference)
      • 幽灵引用或幻影引用。
      • 对象是否存在虚引用,彻底不会对其生存时间影响。
      • 没法经过虚引用得到对象实例。
      • 设置虚引用,该对象被回收时收到一个系统通知。
  • 对象的死亡 -- (何时回收)

    若是对象通过可达性分析以后没有与GCRoot相连的引用链,若是须要执行finalize()方法的对象,至少须要经历两次标记过程。(《深刻理解Java虚拟机》原文:即便在可达性分析算法中不可达的对象,也并 非是“非死不可”的,这个时候他们暂时处于“缓刑”阶段,要真正宣告一个对象的死亡,至少须要经历两次标记过程。加粗部分我认为可能会有歧义,因此用了我本身容易理解的说法。)并发

    • 第一次标记:可达性分析以后,而且筛选出须要执行finalize()方法.
          如下状况属于不须要执行finalize()方法:异步

      • 当前对象没有覆盖finalize()方法
      • finalize()方法被虚拟机调用过
    • 第二次标记:若是该对象须要执行finalize()方法,会被放入F-Queue队列中,以后系统执行对象的finalize()方法。再执行玩finalize()方法以后,若是该对象从新被引用,将会被移除“即将回收”的集合。不然,必定会被回收。     关于finalize()方法和F-Queue队列:性能

      • F-Queue中的finalize()方法由虚拟机创建且优先级很低的Finalizer线程执行。
      • 虚拟不会保证该线程执行完成,由于可能对象的finalize()方法执行时间太长,致使内存回收系统崩溃。
      • finalize()方法是对象最后拯救本身,不被回收的机会。
      • finalize()方法只会被系统调用一次。
  • 方法区对象的回收

    • 特色spa

      • 方法区的垃圾手机效率很低
    • 回收内容线程

      • 废弃常量
      • 无用的类
    • 判断是不是废常量server

      • 没有任何对象引用该常量
    • 判断是不是无用的类

      须要同时知足一下条件:

      • 该类全部实例都已经被回收。
      • 加载该类的ClassLoader已经被回收。
      • 该类对应的java.lang.class对象没有在任何地方引用,没法在任何地方经过反射访问该类的方法。 知足以上三个条件并非必定会被回收,虚拟机提供了Xnoclassgc进行控制。
  • 垃圾收集算法 -- (如何回收)

  • 标记-清除算法

    • 两个阶段

      • 标记

        标记出全部须要回收的对象

      • 清除

        标记完成后统一回收全部被标记的对象

    • 缺点

      • 效率低
      • 浪费空间。标记清除后会出现大量的不连续的内存碎片,致使没法分配内存较大的对象从而再一次触发垃圾回收。
    • 复制算法

      • 过程
        • 将可用内存按容量分为大小先相同的内存块,每次只使用期其中的一块。当这一块内存用完时,将全部存活的对象都复制到另外一块,而后清空已使用的内存。
    • 缺点
      • 将内存缩小至原来的一半,代价过高
      • 存活的对象过多时,复制的时间长、效率低。
      • 虚拟机的新生代回收采用复制算法
      • 新生代对象很快就会死亡,因此不须要按照1:1比例划份内存
      • 虚拟机将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor空间
      • 回收时,将Eden和其中一块的Survivor中存活的对象,复制到另外一块Survivor空间。
      • HotSpot虚拟机默认Eden和Survior的大小为8:1.
      • 复制到另外一块Survivor时,若是空间不够,须要其余内存(老年代)进行分配担保。、
  • 标记-整理算法

    • 虚拟机的老年代通常采用标记-整理算法
    • 老年代可能出现对象100%都存活的极端状况,因此内存必须浪费50%空间。

    • 特色

      • 标记-整理算法的标标记过程与标记-清楚算法相同,但后续不是直接对可回收对象进行整理,而是让存活的对象都向一端移动,而后直接清除这一端比边界之外的内存。
  • 分代收集算法

    • 根据对象存活周期的不一样将内存划分为几块,java堆通常分为新生代和老年代,根据年代的特色选取不一样的垃圾回收算法。
  • 收集算法的选择

    • 每次垃圾收集都有大量对象死去,少许存活 ==> 复制算法
    • 对象存活率高。没有额外空间进行分配担保 ==> 标记-清楚 或 标记-整理算法
  • 垃圾收集器

    垃圾收集器是内存回收逇具体实现。虚拟机厂商通常都会提供参数供用户根据本身的应用特色和要求和组合出各个年代所使用的收集器。

    • Serial收集器

      • 最基本、历史最悠久的单线程收集器。Serial收集器在虚拟机Client模式下默认的新生代收集器。
      • 缺点
        • 它在进行垃圾收集时,必须暂停其余全部工做线程,直到它收集结束。
      • 优势
        • 简单、高效(与其余单线程的收集器相比)
    • ParNew收集器

      • Serial收集器的多线程版本,其与行为与Serial收集器彻底一致。
      • 它是虚拟机server模式下首选的新生代收集器
      • 它是除了Serial收集器以外惟一能与CMS收集器配合工做的收集器
      • ParNew收集器在单线程的环境下的性能并不比Serial收集器要好。
    • Parallel Scavenge 收集器

      • 使用复制算法新生代多线程收集器,也称为“吞吐量优先”收集器。
      • 特色
        • 它的目的是达到一个可控制的吞吐量。 吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
        • 高吞吐量能够高效率的使用cpu时间,尽快的完成任务,主要是和在后台运算而不须要太多交互的任务。
    • 三个参数

      • -XX:MaxGCPauseMills:控制最大垃圾收集停顿时间。容许一个值大于0的毫秒数,收集器尽量保证收集停顿时间不超过该值。
        • GC停顿时间是以吞吐量和新生代空间来换取的,因此并非该值越小越好。
      • -XX:GCTimeRadio:设置吞吐量大小。值为0-100的整数,表明垃圾收集时间,占总时间的比率,至关因而吞吐量的倒数。
      • -XX:UseAdapitiveSizePolicy:打开该参数后,GC会采用GC自适应策略,动态调整这些参数以提供合适的停顿时间或者最大的吞吐量。
    • Serial Old 收集器

      • 它是Serial收集器的老年代版本,单线程收集器,使用标记-整理算法。
      • 用途
        • 虚拟机啊Client模式下使用
        • JDK1.5以前与Parallel Scavenge收集器配合使用。
        • 做为CMS收集器的后备预案。
    • CMS收集器

      • CMS(Concurrent Mark Sweep)收集器是一种一获取最短回收停顿时间为目标,提升用户体验。
      • 基于标记-清除算法

      • 运做过程

        • 初始标记
          • 标记GCRoots能直接关联到的对象,速度很快。
        • 并发标记
          • 标记能够回收的对象
        • 从新标记
          • 修正并发标记时,由于程序继续运做致使表动的标记记录。
        • 并发清除
          • 清除被标记能够回收的对象
      • 优势
        • 并发收集
        • 低停顿
      • 缺点

        • 对CPU资源很是敏感。

          在并发阶段,虽然不会致使用户线程停顿,可是会占用一部分CPU资源,致使应用程序变慢,总吞吐量下降

        • 没法处理浮动垃圾。可能出现“Concurrent Mode Failure”失败,而致使另外一次Full Gc 的产生。

          浮动垃圾:并发清理阶段,用户线程并无中止,全部依然会有新的垃圾产生,这部分垃圾CMS收集器没法在当次收集时处理。

          也正是因为用户线程没有中止,因此CMS收集时,虚拟机须要预留一部分空间提供并发收集时的程序运做使用。若是CMS收集期间预留的内存没法知足程序的须要。就会出现Concurrent Mode Failure失败,虚拟机就会启动后备预案:启用Serial Old 收集器来从新进行老年代的垃圾收集,这就致使了丁顿时间很长。

          • -XX:CMSInitiatingOccupancyFraction :提升触发CMS收集器的百分比。JDK1.5默认为68%,JDK1.6默认为92%。当老年代的使用空间比率达到该值,就会触发CMS收集器。
          • 基于上一点的描述,参数XX:CMSInitiatingOccupancyFraction若是设置过高,容易致使出现大量的失败,使性能下降。
        • 会产生大量的空间碎片

          由于CMS收集器i是基于 标记-清除 算法的收集器。因此会差生空间碎片,分配大对象时就会可能由于没有足够的连续空间,致使提早触发一次FullGc

        • 解决办法

          • 解决对CPU资源很是敏感:

            虚拟机提供红了“增量式并发收集器”的CMS收集器变种,在并发标记、清理的时候让GC线程、用户线程交替执行,尽可能减小GC线程的独占资源的时间。可是效果通常,不提倡使用。

          • 解决产生大量的空碎片:

            +XX:+UserCMSCompactAtFullCollection: 开关参数(默认开启),在须要进行FullGC是,开启内存碎片和平整理。内存整理没法异步,因此会使停顿时间变长

            +XX:CMSFullGCBeforeCompaction: 设置多少次不进行碎片整理的FullGC后,跟着来一次进行碎片整理的FullGC.。(0表示每次FullGC都进行碎片整理)

    • G1收集器

      • G1是一款面向服务端应用的垃圾收集器。

      • 特色

        • 并行与并发。
        • 分代收集。

          使用不一样的方式处理不一样代的收集。

        • 空间整合。

          G1总体上是基于“标记-整理”算法实现,从局部看,是基于“复制”算法实现。这意味着G1收集器不会产生空间碎片。

        • 可预测的停顿。

          G1除了追求低停顿之外,还能创建可预测的停顿时间模型。G1将内存分为多个Region,跟踪每一个Region中的垃圾堆积的价值大小,根据容许的手机实现,优先回收价值最大的Region,保证了 G1收集器在有限的时间内能够获取可能高的收集效率。

      • 运做过程

        • 初始标记

          标记GCRoots能直接关联到的对象,且修改TAMS(Next Top Mark Start)的值,让用户程序并发运行时,能在正确的Region中建立新对象。

        • 并发标记

          从GC Root进行可达性分析,找出存活的对象,耗时较长,可是能够和用户程序并发执行。

        • 最终标记

          修正在并发标记期间因用户程序并发运行致使标记产生呢表动的标记记录。须要暂停线程,可是能够并行执行。

        • 筛选回收

          首先对各个Region的回收价值和成本进行排序,根据游湖指望的GC停顿时间来制定回收计划。

    • 若是应用追求低停顿,G1是个可尝试的选择。

    • 若是应用追求高吞吐量,G1并非个很好的选择。

相关文章
相关标签/搜索