垃圾收集器(GC)

来源:互联网 发布:李嘉欣真人知乎 编辑:程序博客网 时间:2024/04/30 06:24

垃圾收集器(Garbage Collection, GC)

1、概述

垃圾收集(Garbage Collection, GC)的历史远远比java久远,很久之前人们就开始思考GC需要完成的三件事情:
*那些内存需要回收?
*什么时候回收?
*如何回收?
到如今,内存的动态分配与内存回收技术已经相当成熟,一切看起来都进入了“自动化”时代,那我们去了解GC和内存分配的目的何在呢?其实当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要实施必要的监控和调节。
java内存运行时区域中的各个部分的内存分配和回收都具备确定性,不需要过多考虑垃圾回收的问题;而java堆和方法区不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存。
堆中几乎存放着java世界所有的对象实例,垃圾收集器在对堆进行回收前,首先要确定哪些对象“存活”着,哪些对象已经死去(即已经没有任何途径使用的对象)。
判断对象是否“存活”的算法:
  • 引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1,当引用失效时,计数器就减1;任何时刻计数器都为0的对象就是不可能在被使用的。
java中没有使用它的原因是:它很难解决对象之间的相互循环利用的问题
  • 根搜索算法
通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象 GC Roots 没有任何引用链相连时,则此对象是不可用的。

下面介绍垃圾收集算法:
  • 标记-清除算法
首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。标记的过程就是判断对象是否存活的方法。
它的主要缺点有两个:一是效率低下,二是标记清除之后会产生大量不连续的内存碎片,如果程序需要分配较大对象时无法找到足够的连续内存,这时候就会再次触发垃圾收集的动作。
  • 复制算法
为了解决标记-清除算法的效率问题,产生了复制算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存使用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
现在的商业虚拟机都是按照这种收集算法来回收新生代的,不过是分成了一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor,空闲的那块Survivor(内存的分配担保)就相当于银行贷款时候的担保人。Eden和Survivor的大小比例是8:1 。
  • 标记-整理算法
复制收集算法在对象存活率较高的时候效率低下,而且需要额外的空间进行分配担保。所以老年代一般不能直接选用这种算法。所以就有了标记-整理算法,标记的过程和标记-清除算法一样,下来是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
  • 分代收集算法
根据各个年代的特点采用最适合的收集算法。在新生代中,每次垃圾收集时都发现大批对象死去,只有少量存活,就选择复制算法,而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用标记-整理算法进行回收。

垃圾收集器

收集算法是内存回收的思想或者说是方法论,垃圾收集器就是内存回收的具体实现。java虚拟机规范中没有对垃圾收集器的实现做出任何规定。

                                                 

这是HotSpot JVM 1.6 的垃圾收集器,两个收集器之间存在连线,就说明它们可以搭配使用。

  • Serial收集器
Serial收集器是最基本的收集器,它是一个单线程的收集器,它在进行垃圾收集的时候,必须暂停其他所有的工作线程(Stop The World),直到它收集结束。Serial收集器的工作过程如下图:


相对于其他收集器的单线程,它简单而高效,现在依然是虚拟机运行在Client模式下的默认新生代收集器。

  • ParNew收集器

ParNew收集器是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样。ParNew收集器的工作过程如下图:

它是许多运行在Server模式下的虚拟机首选的新生代收集器。

  • Parallel Scavenge收集器
Parallel Scavenge收集器也是一个新生代收集器,是使用复制算法的并行的多线程收集器。它的目标是达到一个可控制的吞吐量(CPU用于运行用户代码的时间与CPU总消耗时间的比值)。

  • Serial Old收集器
Serial Old是Serial收集器的老年代版本,也是单线程收集器,使用标记-整理算法。Serial Old收集器的工作过程如下图:
  • Parallel  Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。在注重吞吐量及CPU资源敏感的场合,优先考虑Parallel Scavenge加Parallel Old收集器。Parallel Old收集器的工作过程如下图:


  • CMS 收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。是基于标记-清除算法实现的。CMS收集器的工作过程如下图:


它的优点是并发收集、低停顿,缺点是①对CPU资源非常敏感;②无法处理浮动垃圾;③收集结束时会产生大量空间碎片。

  • G1收集器
G1收集器是基于标记-整理算法实现的收集器,不会产生空间碎片;可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不能超过N毫秒。

G1将整个java堆划分为多个大小固定的独立区域,并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域。

内存分配与回收策略

①对象优先在Eden分配;

②大对象直接进入老年代;

③长期存活的对象将进入老年代

④动态对象年龄判定。

1 0
原创粉丝点击