常用的垃圾回收算法
引用计数法(Reference Counting)
原理:
对于一个对象A,任何对象引用A,则A的引用计数器+1,引用失效则-1,当引用计数为0的时候,则进行回收
缺陷:
不能解决循环引用,当A和B互相引用的时候,引用计数器就不会为0,则无法回收,造成内存泄漏,最终会内存溢出。
可达对象:通过根对象进行引用搜索,最终可以达到的对象
不可达对象:通过根对象进行引用搜索,最终不可达的对象
注意:由于不能解决循环引用,所以Java虚拟机并没有采用
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020092118225277.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xvb2tpbmdOb3J0aA==,size_16,color_FFFFFF,t_70#pic_center)
标记-清除算法(Mark-Sweep)
原理:
标记阶段:标记可达对象,未标记的则时垃圾级;
清理阶段:清除未被标记的对象
缺点:标记-清除算法是对一块连续的内存区域回收,但是回收之后内存时连续的,但大对象进入生代时候,会由于内存不连续,导致效率低
![在这里插入图片描述](https://img-blog.csdnimg.cn/202009211823136.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xvb2tpbmdOb3J0aA==,size_16,color_FFFFFF,t_70#pic_center)
复制算法(Copying)
原理:
将一块内存一分为二,使用的时候,只使用其中一块,等垃圾回收的时候,则把使用的一块内存往空闲的内存排队移动。解决标记清除算法内存不连续的缺点
缺点:
显而易见,内存一分为二,内存变小
所以比较适合,存活对象对象比较少的场景,毕竟内存减半
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921182327263.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xvb2tpbmdOb3J0aA==,size_16,color_FFFFFF,t_70#pic_center)
Java虚拟机应用:
我们知道堆内存逻辑分区是:新生代,老年代;新生代分为:Eden区,survivor区(包含form区和to区)。survivor区则采用这种算法
新生代:存放年轻对象的堆空间;指刚刚创建的对象或者经历垃圾回收次数不多的对象
老年代:存放老年对象;指经历多次垃圾回收还存活的对象
Minor GC:新生代垃圾收集动作,收集的时候会触发STW(停止应用程序的线程,简单来说就是停止服务)
Major GC/Full GC:老年代GC,指发生在老年代的GC动作
垃圾回收的时候,Eden区还存活的对象,会转移到survivor区(假如是from区),survivor区进行垃圾回收的时候,存活的对象转移到survivor区空闲的区域(to区:从一块连续的内存的排队进入),这样保证了内存的连续性,经历多次回收的对象会进入老年代,但大对象或者survivor区其中一块内存放不下的时候剩余的对象也会直接进入老年代,等full gc的就会回收
复制-压缩算法(Mark-Compact)
原理:
标记阶段:标记所有可达对象
移动阶段:把可达对象往内存另一端移动
清理阶段:清除所有不可达对象
适合存活对象比较多的场景,比如老年代
也叫:标记-清除-压缩算法
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921182338993.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xvb2tpbmdOb3J0aA==,size_16,color_FFFFFF,t_70#pic_center)
分代算法(Generational Collecting)
以上几种对的垃圾回收算法,其实都各自的特色
但是Java虚拟机采用的是分代算法,分代算法其实就是以上算法的综合体
原理:
将内存区间根据对象的特点分为几个区域,根据内存区域的特点采用不同的算法
新生代:存活对象少,回收频繁;复制算法
老年代:存活对象多,回收次数少;标记-清除-压缩算法
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921182353388.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xvb2tpbmdOb3J0aA==,size_16,color_FFFFFF,t_70#pic_center)
分区算法(Region)
原理:
按照对象的生命周期划分为两个部分(已使用,未使用);
将整个堆空间划分成连续不同的区间,每个区间都独立使用,独立回收;
一般来说,堆内存的空间越大,一次GC的时间就会越长,触发STW,GC停顿的时间越长,所以为了更好的控制GC停顿的时间,
由于内存分为多个小内存,而不是回收这个堆内存,从而减少GC一次的停顿时间的长度
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921182406912.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xvb2tpbmdOb3J0aA==,size_16,color_FFFFFF,t_70#pic_center)
对象回收条件
什么时候不能回收:
可触及的:从根对象触发,可达到这个对象
可复活对象:对象的所有引用都被释放,但是在对象的finalize()方法中复活
什么时候可回收:
不可触及的:对象的finalize()方法调用以后还没有复活也没有对象对其引用,就可以回收了(finalize方法只能被调用一次)
对象的引用强度
强引用:
就是 new 关键字,并赋值
该对象不会别回收,发生OOM也不回回收,所以容易导致内存泄露
软引用:
SoftReference personSoftReference = new SoftReference<Person>(person);
当堆内存空间不足的时候,就会回收
弱引用:
GC只要发现就回收
虚引用:
GC只要发现就回收