zoukankan      html  css  js  c++  java
  • GC

    什么是GC

     GC是java的内存回收机制。

    为什么会有GC

     在开发中控制对象的生命周期对于程序员来说是很复杂繁重的工作,不恰当的内存回收会导致程序的不稳定;

    而java作为编程语言之初,把便捷的开发作为发展的方向,所以设计了GC帮我们完成了所有对象的清理工作。

    GC相关知识

    垃圾收集算法


    标记-清除(Mark-Sweep):

    标记和清除是两个阶段:首先标记出所有需要回收的对象,然后统一回收所有被标记的对象。

    标记清除算法适合少量垃圾的回收中使用;

    在垃圾较多的回收中效率很低;另外它会产生大量不连续的内存空间,这可能会导致在分配大对象的时候无法找到内存而触发GC。

    复制算法(Copying):

    复制算法会把可用内存分为A、B两部分,当A内存用完了,就把这一部分还存活的对象复制到B内存。

    复制算法简单高效,适合大量垃圾的回收中使用;

    缺陷是对内存的利用率不高,相当于只使用了一半的内存(在HotSpot中,是将内存分为一块较大的Eden空间和两块较小的Survivor空间,Eden与Survivor的比例是8:1,每次使用Eden和其中一块Survivor,回收时将Eden和Survivor中还存活着的对象一次性地复制到另外一块To Survivor空间上,再清理掉Eden和From Survivor空间);

    另外在垃圾比较少、甚至没有垃圾的情况下,回收的性能很差:没有垃圾的极端情况下,复制算法不仅没有回收垃圾还要把A内存的所有对象(此时都是存活对象)复制到B内存。

    标记-整理(Mark-Compact):

    标记整理算法可以理解为标记-整理-清理的组合,它和标记清理算法一样,但后续步骤不是直接清理,而是多了整理的过程:让所有存活的对象都向一端移动,再清理另一端的回收对象。

    标记整理算法和沿袭标记清理算法,所以也只适合回收对象较少的场景使用;

    另外附加的整理过程让它解决了标记清理算法中碎片空间问题。

    分代收集(Generational Collection):

    分代收集算法被现在的虚拟机所采用,它把堆内存分为新生代、老年代两部分,然后根据新生代、老年代各自的特点使用合适的垃圾收集算法:

    新生代的特点是存活率低,可回收的对象很多,所以当前虚拟机采用了复制算法;

    老年代的特点是存活率高,可回收的对象很少,所以采用标记清理或标记整理算法。

    垃圾收集器


     垃圾收集器是垃圾收集算法的具体实现。

    SerialNew(串行):

    Serial是一个单线程的串行收集器,它是采用复制算法实现的新生代收集器;

    进行垃圾收集的时候必须停止其他线程的工作;

    不过Serial比起其他收集器的单线程更高效,适用于单核CPU且虚拟机内存较小的Clinet模式。

    ParNew:

    它是SerialNew收集器的多线程版本;

    也是在收集的时候会停止其他线程的工作;

    不过ParNew是唯一能与CMS收集器搭配;而且在多核CPU上性能优于Serial,适用于Server模式的新生代中。

    Parallel Scavenge(并行清理):

    Parallel Scavenge是一个多线程的并行收集器,它也是采用复制算法实现的的新生代收集器,

    与parNew等收集器关注的减少用户线程的停顿时间不同,Parallel Scavenge是关注减少GC发生次数,提高吞吐量;

    Parallel Scavenge减少了GC次数但让用户线程的停顿时间加长了,所以不适合交互频繁的场景;

    不过它适用于后台计算这种提高吞吐量的场景;

    另外自适应调节优化也是Parallel Scavenge的特色。

    SerialOld:

    SerialOld是相对于SerialNew的老年代收集器,使用标记-清理算法;

    另外SerialOld的用途是作为CMS收集器的备用方案,在CMS并发收集发生Concurrent Mode Failure时使用。

    ParallelOld:

    Parallel Old是相对于Parallel Scavenge的老年代收集器,使用“标记-整理”算法。

    与SerialOd不同的是,ParallelOld使用了多线程,比起SerialOld在Server模式下作为老年代收集器更具优势; 

    另外可以与ParallelScavenge组合成吞吐量优先收集器;

    Concurrent Mark Sweep(并发标记清除):

    CMS 是一个并发收集器,采用标记-清理算法实现的老年代收集器;

    由于CMS是并发的,所以对CPU要求很高;

    另外CMS无法处理浮动垃圾(GC阶段产生的新垃圾称为浮动垃圾),所以CMS提供了阈值设置老年代的预留空间,当达到了阈值便会出现“Concurrent Mode Failure”失败而导致另一次Full GC(FullGC可能伴随)的产生,不过此时会启用SerialOld备用方案;

    还有CMS是基于标记清理算法实现,所以会产生碎片空间问题,CMS也提供了参数在GC之前来整理碎片空间;

    不过CMS进行GC的时候不会停止用户线程的工作,所以交互过程的响应很快,非常适合BS架构的服务端;

    GI:

    GI是当今收集器技术发展的最前沿成果之一,HotSpot开发团队赋予它的使命是未来可以替换掉JDK 1.5中发布的CMS收集器;

    由于现在市场还没有大面积商业使用,暂时不做介绍。

    总结:

    GC收集器种类众多,和其他更具一样,没有万能的,只有理解各种GC收集器的特性,才能在不同场景根据实际环境选择;

    GC的策略大体上分为响应速度优先和吞吐量优先的两种组合:

    响应速度目前最成熟的老年代、新生代搭配是CMS + ParNew,因为CMS是基于并发实现,不会停止用户线程,所以响应很快,这种组合也是一种限制,因为CMS可搭配的只有ParNew;

    吞吐量的组合则是ParallelOld + ParallelScavenge,因为ParallelScavenge目的是为了减少GC次数,从而提高吞吐量,ParallelOld由于使用了标记整理算法所以没有碎片空间这一问题,另外对于SerrialOld而言是多线程的,在多核CPU环境下性能更好。

    哪些内存需要GC

    在了解GC具体回收哪些对象的时候,要先弄清楚可达性算法。

    可达性算法


    可达性算法的思路是以一系列的GCRoots对象为起点,然后从这些节点往下搜索,当一个对象从GCRoots搜索不到,则证明这个对象是可以被回收的;

    如下图的object5、6、7是可以回收的:

    总结


     GC回收的是超出作用域,在GCRoots搜索不到,且经过一次回收标记仍旧没有复活的对象。

    GC什么时候进行回收

    在JVM内存空间不足的时候、调用System.gc()的时候会触发;

    另外可以通过调优,控制新生代进入老年代的年龄、设置Eden与Survivor的比例、设置老年代预留空间,来触发CMS进行FullGC(老年代的GC,另外MinorGC是新生代的GC,一般FullGC会伴随至少一次的MinorGC)。

    GC是怎么回收的

     GC会对新生代、老年代两部分进行回收动作:

    如果是新生代内存空间不够,会通过复制算法执行MinorGC,即先把Eden还存活的对象复制到ToSurvivor,再清理掉Eden和FromSuvivor;

    如果是老年代的内存空间不够则使用标记清理算法执行FullGC,首先对回收对象进行标记,标记过后仍未使用的对象清理掉,之后再整理碎片空间,一般FullGC会伴随至少一次的MinorGC。

  • 相关阅读:
    Ubuntu 更换软件源
    Ubuntu 配置 SOCKS5
    Ubuntu 配置 Sha-dow-socks
    frp(内网穿透)
    solr 远程代码执行(CVE-2019-12409)
    多线程处理爬虫
    python实现文件自动排序
    python 实现根据文件名自动分类移动至不同的文件夹
    Centos7如何开启任意端口服务
    centos7默认安装没有连接网络
  • 原文地址:https://www.cnblogs.com/dahuandan/p/7103509.html
Copyright © 2011-2022 走看看