什么是垃圾?为何要回收?java
在java中,当一个对象没有被任何指针指向的时候,他就成为了一个垃圾,也就应该对其进行回收,若是不对其进行回收就会形成内存泄露,甚至是内存溢出。程序员
垃圾回收分为两个阶段垃圾标记和垃圾回收。算法
引用计算算法(java并未使用,不存在循环引用形成内存泄露)数据库
对每个对象保存一个整型的引用计数器属性,用于记录对象被引用的状况。当对象被任何一个对象引用就会对计数器加以,引用失效就减一,直到计数器为0,就把该对象视为垃圾,对其进行回收。网络
该算法具备效率高,没有回收延迟的特性,可是他却增大了开销,每次进行加减法也增大了时间复杂度,同时,他不能处理循环引用的问题。因此实际上java并未采用这样的算法。多线程
可达性分析算法并发
以根对象集合为起始点,按照从上到下的方式搜索根对象集合所链接的目标对象是否可达。内存中存活的对象都直接或者间接同根对象链接在一块儿。不然,就认为该对象应该被回收。jvm
那么根元素集合包括哪些?ide
虚拟机栈中引用的对象。性能
本地方法栈内引用的对象。
方法区中静态属性引用的对象。
方法区中常量引用的对象。
全部被同步锁持有的对象。
java虚拟机内部的引用。
对象的finalization机制
一个对象在被回收以前会执行finalization()方法,来进行资源释放等操做。
虽然对象的finalization机制听起来很高大上,可是咱们平时好像历来没有用过,没有用过就对了,由于这是给垃圾回收调用的,并不须要程序员调用,并且也不容许程序员调用。由于finalization可能会致使对象复活,其次该机制发生的时间是由GC线程决定的,若是不发生GC就永远不会发生这个机制,最后,若是这个机制使用不当,就会严重影响GC性能。
对象的三种状态
可触及状态
可复活状态
不可触及状态
虚拟机在断定一个对象是否被回收的时候,须要进行两次标记,第一次,判断该对象从根对象集合开始有没有引用链,,而后判断对象是否须要执行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));
虚引用(对象回收跟踪)
虚引用顾名思义,就是形同虚设。与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。
应用场景是在单核CPU的状况下使用一条收集线程去完成清理工做,在垃圾回收期间,其余全部的工做线程都要停下来,直到垃圾回收线程完毕。在其特殊的工做场景下,他的回收效率是最高的,由于他不须要反复进行多线程的切换。一般和Serial Old GC搭配工做。
采用并行回收的方式,其余跟serial回收器十分相似。主要运用在多CPU的环境下。能够和CMS GC以及 Serial Old GC搭配工做。
一个采用了复制算法,并行回收和stw机制的垃圾回收器。由此可知,该回收器的目标是注重吞吐量,
他和采用了标记-压缩算法、并行回收、stw机制的Parallel Old 收集器搭配, 效果很是好。(是jdk8的默认垃圾回收器)
第一款能让垃圾回收线程和用户线程同时进行工做的回收器。他的关注点是尽量缩短垃圾回收时用户线程的停顿时间,提升响应速度。
采用标记-清除算法,因此在垃圾回收以后会产生一些碎片空间,只能引用空闲列表的方式来执行后续的内存分配。
对CPU的资源也很敏感,在并发阶段由于会占用一部分线程,从而使程序变慢,总吞吐量也会下降。
没法处理浮动垃圾。在第二阶段的并发标记过程完成后,由于用户线程的某些操做使得某些以前不是垃圾的对象变成了垃圾,可是第三阶段的重写标记只是进行对以前怀疑是垃圾的对象确认一下是不是真的垃圾,不会标记新产生的这部分浮动垃圾。也就不会回收这部分垃圾。
流程:
初始标记
并发标记
从新标记
并发清除
一个并行回收器,将堆内存划分红多个不想关的区域(物理上不连续),不一样的区域表示不一样的堆分区(Eden,s0,s1,old),这样作的目的是避免在堆中作全区域的回收,经过跟踪每一个区域的回收价值(回收得到的空间大小以及所需时间比例)维护一个优先级列表,根据容许回收的时间,选择回收价值大的区域进行回收。
特色:
并行性
并发性
分代收集
空间整合
可预测的停顿时间模型
回收环节:
年轻代GC
老年代并发标记
混合回收