垃圾回收概念与算法
常用的垃圾回收算法:引用计数法,标记压缩法,标记清除法,复制算法和分代,分区的思想。
1.引用计数法:
最为古老的一种垃圾收集方法,引用计数器的实现很简单,对一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,计数器减1。
存在的两个严重问题:
(1):无法处理循环的引用的情况,因此在java虚拟机中,没有使用这种算法。
(2):引用计数器要求在每次引用产生和消除的时候,需要伴随一个加法和减法操作,堆系统性能会有一定的影响。
循环引用eg:
可达对象:指通过根对象进行引用搜索,最终可以达到的对象。
不可达对象:指通过根对象进行引用搜索,最终没有被引用的对象。
2.标记清除法
分两个阶段:标记阶段,清除阶段
标记阶段:首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是为被引用的垃圾对象。
清除阶段:清除所有未被标记的对象。
存在的问题:可能产生的最大问题是空间碎片
工作过程:
3.复制算法
核心思想:将原来的内存空间分为两块,每次只使用一块,在垃圾回收时,将正在使用的内存中存活的对象复制到未使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
复制算法使用于垃圾对象存在多的时刻,这样复制算法效率是很高的。
在java的新生代串行垃圾回收器中,使用了复制算法的思想,新生代的eden,form,to,空间,3个部分,其中form和to空间可以视为用于复制的两块大小相同、地位相等、且可进行角色互换的空间块。
复制算法过程图:
注意 :复制算法比较适用于新生代,因为新生代垃圾对象通常多余存活对象。复制算法效果会比较好。
4.标记压缩算法
复制算法的高效性是建立在存活对象少、垃圾对象多的前提下,这种情况在新生代经常发生。但是在老年代中大部分都是存活对象,使用复制算法性价比不高。
标记压缩算法在标记清除算法的基础上做了一些优化。会将最后存活的对象压缩到内存的一端,之后清除边界外空间,完成回收工作。
5.分代算法
基本思想:它将内存空间根据对象的特点分为几块,根据每块内存空间的特点,使用不同的回收算法,提高垃圾回收的效率。
内存分代:
为了支持高频率的新生代回收,虚拟机可能使用一种叫作卡表(Card Table)的数据结构。卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代 的引用。在垃圾回收时,只需扫描有引用的老年代空间,使用这种方式,可以大大加快新生代回收速度。
6.分区算法
按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间,每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一个回收多少个小空间。
一般在相同条件下,堆空间越大,一次GC时需要的时间就越长,从而产生的停顿也越长,为了更好的控制停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干小空间,而不是整个堆空间,从而减少一次所产生的停顿时间。
示意图:
谁才是真正的垃圾,判断一个对象的可触及性
可触及性包含三个状态:
(1)可触及的:从根节点开始,可以到达这个对象。
(2)可复活的:对象所有引用都被释放,但是对象有可能在finalize()函数中复活。
(3)不可触及的:对象的finalize()函数被释放,并且没有复活,那么就会进入不可触及的状态,不可触及的对象不可能被复活,因为finalize()函数只会被执行一次。
以上三种状态,只有在对象不可触及时才可以被回收。
对象复活:对象有可能调用finalize()函数复活自己,但是finalize()函数是一个糟糕的模式,调用finalize()时间不明确,不是一个好的资源释放方案,推荐在try-catch-finally中进行资源释放。
引用和可触及性的强度
java中提高四个级别:强引用,软引用,弱引用和虚引用。
强引用就是程序中一般是使用的引用类型,强引用的对象是可触及的,不会被回收。相对的软引用、弱引用和虚引用的对象是软可触及的,弱可触及的,虚可触及的,在一定条件下,都是可以被回收的。
StringBuilder str = new StringBuilder("hello world");
局部变量str指向StringBuilder实例所在的堆空间,通过str操作StringBuilder,那么str就是StringBuilder的强引用,
特点:
(1):强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指的对象。
(2):强引用可以直接访问对象。
(3):强引用可能导致内存泄漏。
软引用:当一个对象只持有软引用,那么当堆空间不足时,就会被回收,使用SoftReferenc
第一次System.gc()不会回收u对象,而当第二次System.gc()时,堆内存空间不足,会导致回收u对象。
每一个软引用都可以附带一个引用队列,当对象的可达性状态发生变化时,(由可达变为不可达),软引用对象就会进入引用队列,通过引用队列,可以追踪对象的回收情况,
弱引用
弱引用是一种比较引用较弱的引用类型,在系统GC时,只要发现弱引用,不管堆空间内存使用情况,都会回收弱引用对象,由于垃圾回收线程优先级低,因此,不一定会立即回收,一旦一个弱引用对象被垃圾回收,便会加入到一个注册的引用队列中(类似于软引用)。引用类用WeakReference类实现。
虚引用
虚引用是所有引用中最弱的一个,一个持有虚引用的对象和没有引用是一样的,当一个虚引用对象调用其get()方法时总会出差,并且虚引用必须与引用队列一起使用,它的作用是跟踪垃圾回收过程。
当一个对象的finalize()函数中可复活的对象,构建对象的虚引用,并指定了引用队列。
垃圾回收时的停顿现象:Stop-The_World实例
在垃圾回收时,都会差产生应用程序的停顿,整个程序会被卡死,即"Stop_The_World",简称:STW
当设置的虚拟机参数不同时,会影响GC发生的时间和频率,进而影响程序的停顿。