zoukankan      html  css  js  c++  java
  • JVM(2)——GC算法和收集器

    一、引入

    上篇博客《JVM——简介》中主要介绍了JVM的内存模型,思考一下:


    为什么要划分堆、栈、方法区等?
    为什么把不同种类的数据信息分别存放?

    答案可以分为很多很多条,这里就说一个方面,如果我们是如何区分数据的种类的,那就是作用域。比如:堆、方法区是线程共享的,而栈是私有的。

    那么管理又包括哪些方面呢?包括创建、存储、回收?这篇博客就来谈谈垃圾回收(Garbage Collection)。

    小编建议各位读者把自己当成GC,那个以回收垃圾为工作的人,这么说貌似有点……

    二、算法

    问自己三个问题:

    what——回收什么
    how——怎么回收
    when——什么时候回收

    1、What

    作为GC,我们要回收些什么?对象实例、变量、类型信息……你怎么知道哪些要回收哪些不需要回收呢,是不是还应用该对象实例,这个对象死了吗?

    判断一个对象死了吗,听着很简单,做起来就不是那么一回事了。

    1)计数器

    给每个对象实例绑一个计数器,如果有人引用他,就加1,不再引用之后,就减1。那么当值为0时,应该就是不被使用的了。

    实现简单,效率也高,但解决不了循环引用的问题。即A引用B,B引用A,按照这种思路,他们就要永久的绑在一起了。

    2)引用链

    假定有一个Root节点,作为起始点,向下搜索,当某个对象没有在这条链上时,即他怎么都走不到Root的时候,我们就说他是不再使用的,可以被回收。

    懒得画了,从网上找了个图。
    这里写图片描述

    2、How

    1)标记-清除

    两个阶段:先标记,后统一回收。

    这个办法很简单很基础,但真的是不怎么看好。时间上,两个过程效率都不高;空间上,会产生大量的空闲碎片,不利于再次使用。

    那怎么办?看后面的方法对他进行改进。

    2)复制

    主要解决效率问题。

    先将内存划分相同大小的两块区域,只使用其中一块。当这一块内存用完了,将对象拷贝到没使用的那块内存区域上,然后进行清理。

    这里写图片描述

    不足之处很明显了,内存很宝贵的呀!!!这简直是复制算法的致命伤,
    提出这个观点的估计是个没受过穷的富家公子啊。

    3)标记-整理

    主要解决了内存碎片问题。

    他的过程前半部分跟标记-清除一样,就在清理之后,让还活着对象移动到一端,把碎片问题解决了。

    当然,效率肯定没有标记-清除好了,不过平衡了一下,这个算法还不错。

    3、When

    GC不能精准的控制回收的具体时间,但分代收集可以控制到一个回收的频率。

    言归正传,文章最开始说到了堆和栈中存放的数据作用域(生命周期)是不同的,那么他们的回收频率肯定不一样。其实实际上要更复杂一点,堆内部的对象实例存活的时间也各有不同,如果每次回收都扫描一遍,那效率是十分低下的。基于这一点,引出著名的分代收集的算法。

    分代收集

    大致分为三个年代:新生代、老年代和永久代。新生代中又分出两个区域:S0(Survivor0)、S1(Survivor1)。个别名字可能翻译的不同,理解就好。

    这里写图片描述

    新生代

    Eden:伊甸园的意思,这里一般存储新创建的对象。这些对象有两种结局,要么被收集清理掉,要么移到下一个Survivor Space中。

    Survivor:幸存者,大致是说他们已经死里逃生一次了。

    老年代

    Old Space:在新生代中对象达到一定比例后,就会将多余的对象移入老年代。

    永久代

    前面两种都是存放在堆中的,因此,又可以把老年代看作是新生代的“备用仓库”。而永久代是在方法区中的,回收频率是最慢的。

    各个年代有各个年代的特点,他们也就可以选择适合自己的算法来进行回收。新生代每次回收的数量都很大,可以使用复制算法。老年代对象存活的时间长,空间也不大,就只能使用“标记-清理”或者“标记-整理”了。

    分代收集算法其实还是利用How中的几个基本算法,只是划分区域(年代),更科学的使用收集算法。

    三、收集器

    前面解决了垃圾回收的what、how、when的问题,那么就要开始实打实的干活了!谁去干?怎么干呢?

    收集器就是帮助我们去解决这个问题的,每个特点也是不一样的,在单独介绍之前,我们先来分分类。

    标准 收集器
    年代 新生代、老年代
    工作模式 串行、并行、并发
    碎片处理 压缩、非压缩
    …… ……


    没有最好的收集器,也没有万能的收集器,只有挑选更适合的才是科学的。

    1)Serial

    adj.
    1. 连续的;一连串的;一系列的。
    2.按期出版的;(小说等)连载的;连续刊行的;连续广播的。
    3.分期偿付的。
    4.【计算机】中行的;串联的。

    特点:

    简单;
    单线程;
    新生代。

    2)ParNew

    Serial的多线程版本;
    新生代;

    3)Parallel Scavenge

    Parallel
    adj.
    1.平行的;并行的 (to; with); 【电学】并联的。
    2.同一方向的,同一目的的。
    3.相同的,同样的,相似的,对应的。

    新生代;
    并行;
    重视吞吐量(这个后面说)。

    4)CMS

    Concurrent Low Pause Collector 并发低停顿收集器

    重视停顿时间,响应速度快,带给用户良好体验。
    过程比较复杂,篇幅有限,不再介绍了。

    5)G1

    并发;
    分代收集;
    标记-整理;
    降低停顿时间。

    分类

    上面只是介绍了几个比较典型的收集器,从年代的角度来看一下:

    注意:横线以上是新生代,以下是老年代。收集器之间的连线表示他们之间可以配合使用。

    这里写图片描述

    小结:
    新生代基本采用复制算法,老年代采用标记整理算法。
    两种算法都解决了碎片问题,由于新生代的空间较大,可以采用复制,更高效些。

    从工作方式上来看:

    1. 串行
      垃圾回收时,”Stop the World”暂停所有线程。如:Serial/Serial Old

    2. 并行
      多个线程可以同时执行,适合多CPU。如:Parallel Scavenge

    3. 并发
      部分收集工作可以和用户线程交替执行。如:CMS

    串行只适合小数据量,这是CPU占用率高,效率快。

    怎样挑选适合自己的收集器呢?

    可以从这几个方面考虑:

    Client模式:优先考虑用户体验,响应要快,停顿时间不能太长。
    Server模式:吞吐量优先,更高效率的利用CPU,尽快完成任务,适合后台运算。吞吐量=运行代码时间/(运行代码+GC)。

  • 相关阅读:
    C#调用默认浏览器打开网页的几种方法
    关于百度排名点击器作弊提升百度排名的说明
    网站被百度降权的各种原因大解析
    百度搜索引擎排名原理、因素
    【ASP.NET开发】获取客户端IP地址 via C#
    防抖和节流
    vuex
    watch 和 computed
    JavaScript捕获与冒泡与委托
    XSS攻击
  • 原文地址:https://www.cnblogs.com/saixing/p/6730213.html
Copyright © 2011-2022 走看看