java垃圾回收机制简述

java垃圾回收机制简述

  • JVM内存模型
  • 如何定位内存垃圾?
    • 引用计数法
    • 可达性分析法
      • 何为GC_Root对象呢?
  • 垃圾回收算法
    • 标记清除法
    • 复制算法
    • 标记-整理法
    • 分代算法

JVM内存模型

java文件经过编译生产class文件,然后通过ClassLoader加载到内存中,然后经过检查,链接,初始化后,最终进入运行时状态.我们说的JVM内存模型就是基于运行时状态来说的,大致结构如下图所示:
java垃圾回收机制简述_第1张图片

主要分为两大区域:

  • 线程共享区域(也称之为主内存):方法区,堆区
    • 方法区:存储类,方法,常量
    • 堆区:绝大部分的对象分配区域,也是垃圾回收分析的主要区域
  • 线程私有区域(也称之为工作内存):
    • 虚拟机栈:java方法调用的栈,记录java方法调用的状态,存储有方法出口,局部变量表,动态链接,操作数栈
    • 本地方法栈:native方法调用的栈
    • 程序计数器:程序执行到的位置

如何定位内存垃圾?

java自带内存垃圾回收机制,那么垃圾回收机制又是怎么确认何为内存垃圾呢?下面介绍两种常见的垃圾定位方法

引用计数法

引用计数法就是,对象没被引用一次,则计数器加1,被解除引用一次则计数器减1,最后判断引用计数器的值是否为0,等于0,则表示为垃圾内存需要回收,否则不回收,但是这个会造成一个问题,那就是循环引用的对象,不会被判定为垃圾,比如A引用B,B引用C,C引用A,此时这三个对象并没有被其他对象引用,应该被回收,但是各自的引用计数器都不等于0,所以并不常用

可达性分析法

可达性分析法,凡是没有直接或间接被GC_Root引用的对象,则为垃圾对象,直接引用就是GC_Root直接指向某个对象,比如GC_Root引用A,间接引用就是GC_Root引用A,A引用B,B引用C,则B和C都间接被GC_Root引用.

何为GC_Root对象呢?

一般一下几种情况就是GC_Root对象

  • 被静态变量引用的对象
  • 被静态常量引用的对象
  • 常量池中的对象
  • 虚拟机栈中引用的对
  • 本地方法栈中引用的对象
  • 正在运行的线程中引用的对象

垃圾回收算法

垃圾回收由于目标内存的特点,一般会使用不同的垃圾回收算法,下面介绍常见的集中垃圾回收算法

标记清除法

标记清除法,顾名思义就是先遍历标记出垃圾内存,然后在遍历清除被标记的垃圾内存,这种算法简单,但是会造成内存的不连续,浪费了大量的空间(特别是有大量小的内存间隙的时候,经常出现有时候需要申请的内存比内存间隙大),所以一般不推荐使用这种算法

复制算法

复制算法,就是将内存分为均等的两份,开始分配内存的时候使用一份,内存满了开始垃圾回收,也是先遍历标记垃圾内存,然后将不是垃圾内存的对象复制到另一份内存中,清除掉第一份内存,此时内存分配工作将转移到第二份内存中,由于是复制存在的内存,所以不会有内存不连续的问题,但是整体复制,耗时大增,特别是遇到生存周期长的大内存对象,会比较耗时,而且内存利用率也不高,始终会有一半的内存空闲

标记-整理法

标记整理法,先遍历标记出垃圾对象,然后将保留对象复制到内存的另一端,再清楚掉区域意外的内存区域,解决了标记清除法内存不连续的问题,但是也要复制一部分对象,所以也会有一定的耗时,但是相比复制算法,提高了内存的利用率

分代算法

分代算法,将内存分为年轻带和老年代,再根据各自特点分配不同的回收算法,比如我们程序中大部分对象生命周期都比较段,应该被快速回收,所以内存分配的时候优先分配到年轻代,然后经过一定周期的保留后移到老年代,或者对象太大直接分配到老年代.

  • 年轻代:由于年轻代的生命周期普遍较短,回收频率较高,一般使用复制算法来回收垃圾,减少垃圾回收的时间,一种流行的年轻代内存划分区域为:Eden:S0:S1=8:1:1,内存分配的时候都是在Eden区分配,然后在Eden区域满了,进行垃圾回收的时候,用复制算法,将生存的对象都复制到S0区域中,如果S0区域已满,则直接复制到老年代中,然后清除Eden区域,等待下次Eden区域满了,再将Eden区和S0区域的的生存对象复制到S1区域中,同样的如果S1已满,剩余的的直接复制到老年代,再清楚Eden和S0区域,以此类推,一般一次年轻代的垃圾回收会回收80%以上的对象.
  • 老年代(Old):老年代的对象,一般都是生存周期比较长的对象,一般不会被回收,垃圾回收的调用次数较年轻代的频率也会低很多,所以使用复制整理算法,一般老年代的垃圾回收一般都伴随着一次年轻代的垃圾回收

你可能感兴趣的:(java基础,jmm,垃圾回收)