JVM:垃圾回收算法、垃圾回收器

垃圾回收算法

什么是垃圾?为何要回收?java

在java中,当一个对象没有被任何指针指向的时候,他就成为了一个垃圾,也就应该对其进行回收,若是不对其进行回收就会形成内存泄露,甚至是内存溢出。程序员

垃圾回收分为两个阶段垃圾标记和垃圾回收。算法

垃圾标记

引用计算算法(java并未使用,不存在循环引用形成内存泄露)数据库

对每个对象保存一个整型的引用计数器属性,用于记录对象被引用的状况。当对象被任何一个对象引用就会对计数器加以,引用失效就减一,直到计数器为0,就把该对象视为垃圾,对其进行回收。网络

该算法具备效率高,没有回收延迟的特性,可是他却增大了开销,每次进行加减法也增大了时间复杂度,同时,他不能处理循环引用的问题。因此实际上java并未采用这样的算法。多线程

可达性分析算法并发

以根对象集合为起始点,按照从上到下的方式搜索根对象集合所链接的目标对象是否可达。内存中存活的对象都直接或者间接同根对象链接在一块儿。不然,就认为该对象应该被回收。jvm

那么根元素集合包括哪些?ide

  • 虚拟机栈中引用的对象。性能

    • 例如,各个线程调用方法中使用到的参数,局部变量等。
  • 本地方法栈内引用的对象。

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

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

    • 字符串常量池中的引用。
  • 全部被同步锁持有的对象。

  • java虚拟机内部的引用。

对象的finalization机制

一个对象在被回收以前会执行finalization()方法,来进行资源释放等操做。

虽然对象的finalization机制听起来很高大上,可是咱们平时好像历来没有用过,没有用过就对了,由于这是给垃圾回收调用的,并不须要程序员调用,并且也不容许程序员调用。由于finalization可能会致使对象复活,其次该机制发生的时间是由GC线程决定的,若是不发生GC就永远不会发生这个机制,最后,若是这个机制使用不当,就会严重影响GC性能。

对象的三种状态

  • 可触及状态

    • 正常使用的对象
  • 可复活状态

    • 对象不可触及,可是在finalization中被复活
  • 不可触及状态

    • 对象不能被复活,真正要被回收以前的状态。

虚拟机在断定一个对象是否被回收的时候,须要进行两次标记,第一次,判断该对象从根对象集合开始有没有引用链,,而后判断对象是否须要执行finalization()方法,若是该对象没有重写finalization()方法,活着以前已经调用过该方法(这个方法只能被调用一次),那么就认为不须要执行,那么该对象也就达到了第三种状态。

若是该对象重写了finalization()方法,且未执行过,那么该对象就会被插入一个队列中,由虚拟机自动建立一个低优先级的finalizer线程触发其finalization()方法,进行第二次标记,若是方法中,对象忽然复活,就会将其移除即将回收的集合,达到第二个复活状态。

垃圾清除

标记清除算法

将全部具备引用执行的对象进行标记,对未标记的的对象进行清除。

因为清除阶段须要对全部对象进行遍历,因此效率不高,并且清除的空间是碎片化的,须要维护一个空闲列表。

复制清除算法

对全部存活的对象不进行标记,而是直接将其复制到另一块如出一辙大小的空间中去,而后对原来空间进行总体回收。这样作的好处是没有标记和清除过程,使得清除十分高效,复制过去的内存空间是连续的,不须要维护空闲表。可是须要两倍的内存空间,并且对象的复制会到值栈中引用地址会发生变化。

标记压缩算法

复制算法创建在存活对象少,垃圾比较多的状况下。该算法就是在标记清除算法的基础上进行了碎片整理,这样就不须要维护空闲列表。可是效率比标记清除算法更低。

分代收集算法

按照不一样的分区选择不一样的算法。在新生区,因为对象具备朝生夕死,回收频率高的特性,因此能够采用回收效率较高的复制算法。而对于老年区,因为对象的生命周期较长,并且空间比较大,回收频率不高,咱们就能够采用标记清除,标记压缩算法或者混合使用。

增量收集算法

还是传统的标记-清除算法和复制算法,只是经过经过分阶段的方式处理线程冲突问题,容许垃圾回收线程分阶段完成清理和复制。可是频繁的进行线程切换会形成垃圾回收的总成本升高,系统的吞吐量降低。

分区算法

将堆空间划分为不少个小空间,根据目标的停顿时间,每次合理的回收若干个小区间,从而减小一次GC所产生的停顿。

内存泄露

当一个对象不被使用的时候,jvm会自动对其进行回收,可是若是出现一些意外状况致使垃圾回收器没有对其回收,这样的现象,咱们就叫他内存泄露,简单来讲,就是对象不能被使用了,可是还不能回收掉,长期占用必定的内存空间。那么在哪些状况下会产生内存泄露?

当存在一些复杂的指针指向,若是存在对象不使用了,因而不少指针都放开,可是某一个指针没有放开,致使这些对象中。仍然有部分指针没有断开,而形成对象不能被回收。

另外,在单例模式中,单例的生命周期和应用程序的周期是同样长的,因此单例程序中,若是持有对不对象的引用的状况,那么这个外部对象就不能被回收,就会致使内存泄露。

还有一些就是资源未关闭链接的行为,数据库链接,网络链接,io操做等,就会致使对象不会被回收(主要是一些和外部资源有链接的对象)。

引用

强引用

相似new出来的对象,只要引用还在就永远不会进行回收。

Object o = new Object();

软引用

若是出现内存不足就会对软引用对象进行回收。

SoftReferencre<User> usersoftRef = new SoftReference<User>(new User("zero",18));

弱引用

生命周期仅存在于下一次垃圾回收以前,也就是只要发生GC就必定会回收弱引用。

WeakReference<User> userweakreference = new WeakReference<User>(new User("zero",18));

虚引用(对象回收跟踪)

虚引用顾名思义,就是形同虚设。与其余几种引用都不一样,虚引用不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。

垃圾回收器

serial回收器(串行回收)

应用场景是在单核CPU的状况下使用一条收集线程去完成清理工做,在垃圾回收期间,其余全部的工做线程都要停下来,直到垃圾回收线程完毕。在其特殊的工做场景下,他的回收效率是最高的,由于他不须要反复进行多线程的切换。一般和Serial Old GC搭配工做。

ParNew回收器(并行回收)

采用并行回收的方式,其余跟serial回收器十分相似。主要运用在多CPU的环境下。能够和CMS GC以及 Serial Old GC搭配工做。

Parallel Scavenge回收器(吞吐量优先)

一个采用了复制算法,并行回收和stw机制的垃圾回收器。由此可知,该回收器的目标是注重吞吐量,

他和采用了标记-压缩算法、并行回收、stw机制的Parallel Old 收集器搭配, 效果很是好。(是jdk8的默认垃圾回收器)

cms回收器(并发收集器)

第一款能让垃圾回收线程和用户线程同时进行工做的回收器。他的关注点是尽量缩短垃圾回收时用户线程的停顿时间,提升响应速度。

采用标记-清除算法,因此在垃圾回收以后会产生一些碎片空间,只能引用空闲列表的方式来执行后续的内存分配。

对CPU的资源也很敏感,在并发阶段由于会占用一部分线程,从而使程序变慢,总吞吐量也会下降。

没法处理浮动垃圾。在第二阶段的并发标记过程完成后,由于用户线程的某些操做使得某些以前不是垃圾的对象变成了垃圾,可是第三阶段的重写标记只是进行对以前怀疑是垃圾的对象确认一下是不是真的垃圾,不会标记新产生的这部分浮动垃圾。也就不会回收这部分垃圾。

流程:

  • 初始标记

    • 全部的工做线程都会出现短暂的stw,这个阶段的工做目的是标记全部GC Root能直接关联的对象,一旦标记完成就会恢复全部暂停线程,因为关联的对象比较小,因此速度很是快
  • 并发标记

    • 从GC root直接关联的对象开始遍历整个关联对象图的过程,这个过程耗时较长,可是不须要暂停用户线程,能够与垃圾收集并发运行。
  • 从新标记

    • 修正标记期间,由于用户进程继续运行致使标记变更的那部分对象的标记记录,这个阶段的停顿时间会比初始阶段长,但远比并发标记阶段时间短。
  • 并发清除

    • 清理删除标记阶段判断的已经不使用的对象,释放内存空间。这个阶段不须要移动存活的对象,因此也是能够和用户线程并发执行的。

G1垃圾回收器(区域分代化)

一个并行回收器,将堆内存划分红多个不想关的区域(物理上不连续),不一样的区域表示不一样的堆分区(Eden,s0,s1,old),这样作的目的是避免在堆中作全区域的回收,经过跟踪每一个区域的回收价值(回收得到的空间大小以及所需时间比例)维护一个优先级列表,根据容许回收的时间,选择回收价值大的区域进行回收。

特色:

  • 并行性

    • 在回收期间可有多个GC线程同时工做,利用多核的特性,减小用户线程的stw。
  • 并发性

    • G1拥有于用户线程交替执行的能力,因此整个回收阶段发生不会彻底阻塞程序的执行。
  • 分代收集

    • 将整个堆空间分为若干个区域,每一个区域都表明堆空间的原始分区的一部分,并且他的回收不像其余回收器同样,他是兼顾新生区和老年区的,
  • 空间整合

    • 区域之间采用的是复制算法,而总体是标记压缩算法。在进行垃圾回收以后,不会有碎片化的空间,避免了后续空间分配带来的许多问题。
  • 可预测的停顿时间模型

    • G1除了追求低停顿以外,还创建可预测的停顿时间模型,让使用者在M的时间片断内,让垃圾回收的时间不超过n。

回收环节:

  • 年轻代GC

    • 当Eden内存空间不足的时候,就会进行YGC,这个时候他是一个并行的独占式的收集器,全部的用户线程都会停下来,将Eden中的存活对象放到s区,大对象直接放到老年区。
  • 老年代并发标记

    • 和cms 的清理过程相似
  • 混合回收

    • 会同时回收新生区和老年区的垃圾。
相关文章
相关标签/搜索