浅谈JVM垃圾回收机制

垃圾回收机制:
Java中引入垃圾自动回收机制,将不再被引用的对象进行回收,所以垃圾回收机制主要作用于堆内存。由于GC操作需要消耗一定的时间,java在对对象的生命周期进行分析后,按照新生代,老年代的划分对垃圾进行回收。对新生代的回收称为minor,对老年代的回收称为FULL,GC程序中调用System.gc()强制执行的GC为full,GC不同的对象引用类型,GC采用不同的方法进行回收。
java对象的引用分为4种类型:
强引用:默认情况下,对象采用的均为强引用,类似于Object o=new Object();这类的引用,只要强引用还在,垃圾回收器永远不会回收被引用的对象。(这个对象的实例没有其他对象引用时,才会被回收)
软引用:用来描述一些还有用但非必需对象的。对于软引用关联着的对象,在将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。也就是说,只有在内存不够用时,才会被回收。适用于缓存场景。
弱引用:用来描述非必须引用,在GC时,无论内存是否足够,一定会被回收。
虚引用:一个对象是否有需引用的存在,完全不会对其的生存周期产生影响,也无法通过一个虚引用取得一个对象的实例。虚引用只是用来得知对象是否被GC(这个对象被回收时会收到一个系统通知).

如何判断一个对象可被回收??
(1)引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1.任何时刻计数器值为0时表示对象已死。但是这种算法很难解决对象之间循环引用的问题。因此采用这种方法判断对象存活性存在造成内存泄露问题
循环引用:当两个对象相互引用,即使他俩都不被外界引用,他俩的计数器都不会为0,因此永远不会被回收,而对实际开发者而言,这两个对象已经完全没有作用了。
代码示例:

public class A{
    public B b;
    class B{
        public A a;
    }
    public void fun(){
        A a=new A();
        B b=new B();
        a.b=b;
        b.a=a;
    }
}

当fun()执行时,A和B分别有两个引用,他们的引用计数都是2,fun执行完毕后,a和b两个引用变量失效并修改A和B的引用计数,但他们的引用计数还是1,所以GC无法回收这两个对象,造成资源泄露,所以该方法不可取。
(2)可达性分析算法:这个算法的基本思想是通过一系列称为”GC Roots"的对象作为起始点,从这些节点出发向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(就是从GC Roots到这个对象不可达)时,则证明该对象是不可用的。

生存还是死亡:
即使在可达性分析算法中不可达的对象,也并非是非死不好的,这时它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少经历两次标记过程
(1).如果对象在可达性分析算法中发现没有与GC Roots可达的引用链,那么它将会会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize()或者finalize()已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
(2).如果这个对象被判定为没有必要执行finalize,则这个对象将会被放置在一个F-Queue的队列之中 ,并在稍后有一个虚拟机自动建立的的finalizer线程去执行它。

垃圾收集算法:
(1)标记-清除算法
该算法分为标记,清除两个阶段 :首先标记出所有需要被回收的对象,在标记完之后统一回收被标记的对象。
这种算法主要不足有两个:一是效率问题,标记和清除两个过程的效率都不高。另一个是空间问题,标记清除过后会产生大量的不连续的内存碎片,空间碎片太多可能会导致以后程序运行过程中需要分配较大对象时,没有足够的连续内存而不得不提前出发一次垃圾收集动作。
浅谈JVM垃圾回收机制_第1张图片
(2)复制算法
将内存分为大小相等的两块,每次使用只是用其中一块。当这一块内存用完了,就将还存活的对象复制到另一块内存,然后把使用过的内存清理掉。这样使得每次独对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,这种算法的代价是将内存缩小成了原来的一半,代价太高。
浅谈JVM垃圾回收机制_第2张图片
(3)标记整理算法
复制算法在对象存活率较高时就要进行大量的复制操作,效率太低。所以老年代一般不采用这种算法。
根据老年代的特点,提出了标记整理算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
浅谈JVM垃圾回收机制_第3张图片
(4)分代收集算法
分代收集算法的思想是按对象的存活周期不同将内存划分为几块一般是把 Java 堆分为新生代和老年代(还有一个永久代,是 HotSpot 特有的实现,其他的虚拟机实现没有这一概念,永久代的收集效果很差,一般很少对永久代进行垃圾回收),这样就可以根据各个年代的特点采用最合适的收集算法。
特点:
新生代:朝生夕灭,存活时间很短。
老年代:经过多次 Minor GC 而存活下来,存活周期长。
新生代中每次垃圾回收都发现有大量的对象死去,只有少量存活,因此采用复制算法回收新生代,只需要付出少量对象的复制成本就可以完成收集;老年代中对象的存活率高,不适合采用复制算法,而且如果老年代采用复制算法,它是没有额外的空间进行分配担保的,因此必须使用标记/清理算法或者标记/整理算法来进行回收。

你可能感兴趣的:(浅谈JVM垃圾回收机制)