垃圾回收机制

​ 垃圾收集是java语言核心技术之一,一切都交给jvm来处理。理解即可

​ 判断是否为垃圾有两种算法,第一种是引用计数法,第二种是可达性分析算法。

​ 先是引用计数法,他是比较好理解的一种算法,给引用的对象添加一个程序计数器,引用一次就加一,引用失效就减一,当计数器为0时,就可以当作垃圾回收了。有优点也有缺点,这种机制执行效率高,程序影响小。缺点是无法检测出循环引用。

  public class ReferenceCountingGC {
     
  
        public Object instance = null;

        public static void testGC(){
     

            ReferenceCountingGC objA = new ReferenceCountingGC ();
            ReferenceCountingGC objB = new ReferenceCountingGC ();

            // 对象之间相互循环引用,对象objA和objB之间的引用计数永远不可能为 0
            objB.instance = objA;
            objA.instance = objB;

            objA = null;
            objB = null;

            System.gc();
    }
}

​ 可达性分析算法,建立GC Roots对象作为起点,从这些节点开始搜索,走过的路径称为引用链,没有引用链时就可以被回收了。什么对象可以是GC Roots呢?

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象)

  • 方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象)

  • 方法区中常量引用的对象(可以理解为:引用方法区中常量的所有对象)

  • 本地方法栈中(Native方法)引用的对象(可以理解为:引用Native方法的所有对象)

    ​ 虚拟机栈种引用的对象即是:我们在程序中创建一个对象,对象会在堆上开辟一块空间,同时,这块地址空间作为引用还会保存到虚拟机栈中,如果生命周期结束,那么引用就会从虚拟机栈中出栈

    ​ 静态属性定义的变量就是static定义的属性。虚拟机栈是私有的,所以这种对象的引用会保存在共有的方法区中

    ​ 常量的引用就是使用了 static final关键字,因为他在初始化之后就不会被修改了

    ​ 要在java中调用C或C++代码,因此会使用native方法。jvm内存中专门有一块本地方法区

java引用:

  1. 强引用:宁愿抛出OutOfMemoryError错误,也不回收。
  2. 软引用:内存空间够,不回收,不够的时候便进行回收
  3. 弱引用:发现就回收,但是垃圾回收器是一个优先级很低的线程,因此不一定会快发现
  4. 虚引用:形同虚设,如果对象仅有虚引用,那么她就跟没有任何引用一样

垃圾回收算法:

1、标记清除算法

​ 分为标记和清除两个阶段,改算法从根集合扫描,对存活的对象标记,再扫描整个空间中未被标记的算法进行回收

垃圾回收机制_第1张图片

缺点:

效率:标记和清除两个过程效率都不高

空间问题:仅对不存活的对象进行处理,产生大量不连续的碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大的对象

垃圾回收机制_第2张图片

2、复制算法

​ 将可用内存按容量大小划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间的一次清理掉。

垃圾回收机制_第3张图片

​ 现在商用的虚拟机都采用这种算法来回收新生代

3、标记整理算法

​ 让所有的存活的对象都向一端移动,然后直接清除掉端边界意外的内存,类似于磁盘整理的过程

​ 适用于对象存活率高的场景(老年代)

垃圾回收机制_第4张图片

4、分代收集算法

不同对象的生命周期是不一样的,而不同声明周期的对象位于堆中不同的区域,因此对堆内存中不同区域采用不同的策略进行回收可以提高jvm的执行效率

新生代对象存活率低,就采用复制算法;

老年代存活率高,就用标记清除算法或者标记整理算法。

Java堆内存一般可以分为新生代、老年代和永久代三个模块

垃圾回收机制_第5张图片

新生代:新生成的对象都放在新生代

​ 将任何新对象分配给 eden 空间。两个 survivor 空间都是空的。当 eden 空间填满时,会触发轻微的垃圾收集。引用的对象被移动到第一个 survivor 空间。清除 eden 空间时,将删除未引用的对象。在下一次Minor GC中,Eden区也会做同样的操作。删除未被引用的对象,并将被引用的对象移动到Survivor区。然而,这里,他们被移动到了第二个Survivor区(S1)。此外,第一个Survivor区(S0)中,在上一次Minor GC幸存的对象,会增加年龄,并被移动到S1中。待所有幸存对象都被移动到S1后,S0和Eden区都会被清空。注意,Survivor区中有了不同年龄的对象。

老年代:存放一些声明周期较长的对象,就像上面所叙述的那样,在新生代尽力了N此垃圾回收后扔存活的对象就会被放到老年代中。

​ 老年代与新生代不同,老年代对象存活的时间比较长、比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并、要么标记出来便于下次进行分配,总之目的就是要减少内存碎片带来的效率损耗。

永久代:存放静态文件,如java类,方法等。

你可能感兴趣的