超详细讲解!Java虚拟机JVM知识及高频考点之垃圾回收

简介

在上一篇我们主要讲解了JVM结构和常用的常用JVM调优及辅助工具,那么这篇文章我们主要讲解JVM垃圾回收。

如何判断垃圾?

引用计数器算法

  • 引用计数算法是通过判断对象的引用数量来决定对象是否可以被回收,jvm会对每个对象额外维护一个计数器,每引用加1,每释放一次减1,减为0时,就会被判定为垃圾对象
  • 优点:简单高效
  • 缺点:
    • 需要额外维护一个计数器,浪费内存资源
    • 相互引用会导致内存泄漏
  • 因此目前主流的Java虚拟机都摒弃掉了这种算法

可达性分析算法

  • 这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。如下图所示。

    72762049.jpg

    在Java语言中,可以作为GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中的引用对象。

  • 方法区中的类静态属性引用的对象。

  • 方法区中的常量引用的对象。

  • 本地方法栈中JNI(Native方法)的引用对象

jdk使用的就是可达性分析算法,真正标记以为对象为可回收状态至少要标记两次。

Java的四种引用类型

强引用:

类似于 Object obj = new Object(); 只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

软引用:

SoftReference 类实现软引用。在系统要发生内存溢出异常之前,才会将这些对象列进回收范围之中进行二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。软引用可用来实现内存敏感的高速缓存。

弱引用:

WeakReference 类实现弱引用。对象只能生存到下一次垃圾收集之前。在垃圾收集器工作时,无论内存是否足够都会回收掉只被弱引用关联的对象。

虚引用:

PhantomReference 类实现虚引用。无法通过虚引用获取一个对象的实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

垃圾回收算法

复制算法(Copying)

  • 把内存分成两份,只使用其中一份,这份满了,就会把存活对象复制到另一份,并把满了的一半内存给清空.如下图:

gc_copying.gif

  • 优点:
    • 对象存活率不高简单又高效
    • 不会产生内存碎片
  • 缺点:
    • 浪费一半内存,如果不想浪费一半的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
    • 对象存活率比较高的情况下需要复制大量对象,并重置指针,效率不高。
  • HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1,一般情况下,新创建的对象都会被分配到Eden区。因为年轻代中的对象基本都是朝生夕死的(90%以上),所以在年轻代的垃圾回收算法使用的是复制算法。总结一下就是:
    • 对象存活率不高
    • 并且会频繁的垃圾回收,需要讲究的是效率
    • 新生代同时对复制算法做了优化:
      • eden:from:to = 8:1:1
      • 只浪费了10%内存资源

标记清除(Mark-Sweep)

“标记-清除”(Mark Sweep)算法是几种GC算法中最基础的算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。正如名字一样,算法分为2个阶段:

  • 遍历所有对象标记出垃圾对象。
  • 遍历所有对象清除垃圾对象。

    mark_sweep.gif

  • 优点
    • 相对高效
    • 不会浪费一般内存
  • 缺点:
    • 会产生大量的内存碎片
    • 两次遍历效率不高

标记整理(压缩)算法(Mark-Compact)

标记-整理法是标记-清除法的一个改进版。同样,在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是通过所有存活对像都向一端移动,然后直接清除边界以外的内存。

  • 优点: 标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。
    • 不会浪费一般内存
    • 不会产生内存碎片
  • 缺点:如果存活的对象过多,整理阶段将会执行较多复制操作,导致算法效率降低。
    • 两次遍历效率不高
    • 对象存活率比较高的情况下,需要大量的复制并重置指针
  • 老年代一般是由标记清除或者是标记清除与标记整理的混合实现。

分代收集算法(Generational-Collection)

分代回收算法实际上是把复制算法和标记整理法的结合,并不是真正一个新的算法,一般分为:老年代(Old Generation)和新生代(Young Generation),老年代就是很少垃圾需要进行回收的,新生代就是有很多的内存空间需要回收,所以不同代就采用不同的回收算法,以此来达到高效的回收算法。

  • 内存效率:复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。
  • 内存整齐度:复制算法=标记整理算法>标记清除算法。
  • 内存利用率:标记整理算法=标记清除算法>复制算法。

垃圾收集器

STW:Stop The World,服务停顿时间,所有垃圾回收算法或者垃圾回收器优化时,追求低停顿高吞吐量,后续会提到的词

Serial/ Serial Old收集器

串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。新生代、老年代使用串行回收;新生代复制算法、老年代标记-整理;垃圾收集的过程中会STW。

  • 单线程
  • 串行

20180611160921828.png

Serial/ Serial Old收集器

Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例;新生代复制算法、老年代标记-压缩

866783-20190528164222369-2026364357.png

  • serial的多线程版本
  • 一般结合CMS使用(CMS在下文会解释)

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。

  • 老年代的垃圾回收算法
  • 分成四个阶段
    • 初始标记:
      • 可能产生STW。
      • 标记出GCRoot以及GCRoot可直达的对象
    • 并发标记:
      • 可以和用户请求同时进行,不会产生STW
      • 标记出上一个阶段没有标记到的对象
    • 重新标记:
      • 会产生STW
      • 标记出上一个阶段没有标记到的对象
    • 并发清除:
      • 不会产生STW
      • 清除垃圾对象
  • 使用的是标记清除算法
  • 优点:
    • 低停顿
    • 高吞吐
    • 效率高
  • 缺点:
    • 容易产生内存碎片
      • 解决方法:可以通过参数设置解决,可以设置经过几次垃圾回收使用标记整理

774371-20180821141926826-1266970658.jpg

G1收集器

G1是目前技术发展的最前沿成果之一,HotSpot开发团队赋予它的使命是未来可以替换掉JDK1.5中发布的CMS收集器。与CMS收集器相比G1收集器有以下特点:

  • 空间整合,G1收集器采用标记整理算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。
  • 可预测停顿,这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。

上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。

总结一下特点:

  • 跨新生代和老年代的垃圾回收器
  • 没有连续的新生代和老年,把堆分成不连续的等大小的区块
  • 额外维护一个链表,保存了每个区块的垃圾率,优先回收垃圾率高的区块
  • 标记整理算法
  • 可预测停顿时间
  • 不会产生内存碎片

垃圾回收器的比较

1326194-20181017145352803-1499680295.png

如果两个收集器之间存在连线,则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。 垃圾回收器选择策略 :

  • 客户端程序 : Serial + Serial Old;
  • 吞吐率优先的服务端程序(比如:计算密集型) : Parallel Scavenge + Parallel Old;
  • 响应时间优先的服务端程序 :ParNew + CMS。
  • G1收集器是基于标记整理算法实现的,不会产生空间碎片,可以精确地控制停顿,将堆划分为多个大小固定的独立区域,并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage First)。

图片来自:

image.png

猜你喜欢

转载自blog.csdn.net/wdj_yyds/article/details/132323857
今日推荐