什么是垃圾回收机制

      垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。

什么时候进行垃圾回收

    1.会在cpu空闲的时候自动进行回收;

    2.在堆内存存储满了之后;

    3.主动调用System.gc()后尝试进行回收;

如何判断对象可以被回收

  堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)

  引用计数法

    给对象添加一个引用计数器,每当有一个地方引用,计数器加1。当引用失败,计数器就减1。任何时候计数器为0的对象就是不可能再被使用的。

    这个方法实现简单,效率高,但是目前主流的虚拟机中没有选择这个算法来管理内存,最主要的原因是它很难解决对象之前相互循环引用的问题。所谓对象之间的相互引用问题,例如,除了对象A和B相互引用这对象之外,这两个对象之间再无任何引用。但是它们因为相互引用对方,导致它们的引用计数器都不为0,于是引用计数器无法通知GC回收它们。

      

    优点:

      1.实时性较高,无需等到内存不够的时候,才开始回收,运行根据对象的计数器时候为0,就可以直接回收;

      2.在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outogmember错误;

      3.区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象;

    缺点:

      1.每次对象被引用时,都需要区更新计数器,有一点时间开销;

      2.浪费CPU资源,及时内存够用,仍然在运行时进行计数器的计算;

      3.无法解决循环引用问题;

  可达性分析算法

    这个算法的基本思想就是通过一系列的称为"GC Roots"的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链的话,则证明此对象是不可用的。

    GC Root根节点:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等等。

      

   如何判断一个常量是废弃常量

    运行时常量池主要回收的是废弃的常量。

    假如咋常量池中存在字符串“abc”,如果当前没有任何String对象引用该字符串常量的话,就说明常量“abc”就是废弃常量,如果这时发生内存回收的话,而且有必要的话,“abc”会被系统清理出常量池。

    如何判断一个类是无用类

      需要满足以下三个条件:

        该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例;

        加载该类的ClassLoader已经被回收;

        该类对象的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法;

      虚拟机可以对满足上述三个条件的无用类进行回收,这里仅仅是可以,而并不是和对象一样不适用了就必然会被回收。

垃圾回收算法  

      

  标记-清除算法

    它是最基础的收集算法,这个算法分为两个阶段,“标记”和“清除”。首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

    它有两个不足的地方:

      1.效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的;

      2.通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的;

          

  复制算法

    为了解决效率问题,复制算法出现了。它可以把内存分为大小相同的两块,每次只使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块区,然后把空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

        

 

    JVM中年轻代内存空间

      1.在GC开始的时候,对象只会存在于Eden区和名为From的Survivor区,Survivor区To是空间;

      2.紧接着进行GC,Eden区中所有存活的对象都会被复制到TO,而在From区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年来代中,没有达到阈值的对象会被赋值到To区域;

      3.经过这次GC后,Eden区和From区已经被清空。这个时候,From和To会交换他们的角色,也就是新的To就是上次GC前的From,新的From就是上次GC前的To。不管怎样,都会保证名为To的Survivor区域是空的;

      4.GC会一直重复这样的过程,直到To区被填满,To区被填满之后,会被所有对象移动到年老代中;

    复制算法优点:

      1.在垃圾对象多的情况下,效率较高;

      2.清理后,内存无碎片;

    复制算法缺点:

      在垃圾对象少的情况下,不适用;

      分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低;

  标记-整理算法

    根据老年代的特点提出一种标记算法,标记过程和“标记-清除”算法一样,但是后续步骤不是直接对可回收对象进行回收,而是让所有存活的对象向一段移动,然后直接清理掉边界以外的内存。

        

  分代收集算法

    现在的商用虚拟机的垃圾收集器基本都采用“分代收集”算法,这种算法就是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

    在新生代中,每次收集都有大量对象死去,所以可以选择复制算法,只要付出少量的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,就必须选择“标记-整理”算法进行垃圾手机。

 

 posted on 2019-12-05 15:45  wnwn  阅读(466)  评论(2编辑  收藏  举报