jvm-垃圾回收

垃圾回收概述

垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾,垃圾收集的三个问题:哪些内存需要回收,什么时候回收,如何回收?java堆是垃圾回收的工作重点。

垃圾回收的相关算法

垃圾标记阶段算法

在堆里几乎存放着所有的java对象实例,在执行垃圾回收之前,首先需要区分内存中哪些是存活对象,哪些是已经死亡对象,只有标记为死亡对象,gc才会执行垃圾回收,释放其所占用的空间,当一个对象已经不在被任何存活对象引用时候,就可以宣判死亡,判断对象存活有两种方式

  • 引用计数算法:堆每个对象保存一个完整的整形的引用计算属性,用于记录对象被引用的情况,对于一个对象A,只要引用A,A的引用计数器加一,引用失效减一,当引用计数器为0.表示A不再被使用,可以进行回收。 引用计数器有一个严重的问题,它无法处理循环引用
  • 可达性分析,根搜索算法,追踪性垃圾收集
    GC roots 根集合是一组必须活跃的引用,可达性分析算法是以根对象集合为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达,内存中存活的对象都会被根对象集合直接或者间接连接着,搜索走过的路径叫做引用链,如果目标对象没有任何引用链相连,则是不可达的,意味着对象已经死亡,可以标记为垃圾对象。
    GC roots 包括以下几类元素
  1. 虚拟机栈中引用对象:各个线程被调用的方法中使用到的参数、局部变量
  2. 本地方法栈中引用的对象: java类的引用静态变量
  3. 方法区中常量引用对象:字符串常量池里的引用
  4. 所有同步锁sychronized持有的对象
  5. java虚拟机内部的引用: 基本数据类型对象的class对象,常驻的异常对象,系统类加载器
  6. 其他
    note:由于root采用栈方式存储变量和指针,如果一个指针保存了堆内存里面的对象,但是自己又不存放在堆内存里面,他就是一个root。如果要使用可达性分析算法判断内存是否可以回收,分析工作必须在一个能保障一致性的快照中进行,这点不满足的话分析结果的准确性无法保证,这也是导致GC进行时必须STW 的一个原因

对象的fianlization 机制

允许开发人员提供对象被销毁之前的自定义处理逻辑,当垃圾回收器发现没用引用指向一个对象(垃圾回收次对象之前,总会先调用这个对象的finalize方法),finalize()方法允许在子类中被重写,用于在对象被回收时进行资源释放,永远不要主动调用某个对象的finalize方法,由于finalize 方法存在,虚拟机中的对象一般处于三种可能的状态,

  • 可触及的:从根节点开始,可以达到这个对象
  • 可复活的:对象所有的引用都被释放,但是可能在finalize中复活,但是只有一次复活机会
  • 不可触及的:对象的finalize被调用,并且没有复活,那么就会进入不可触及转态,不可触及的状态不可能被复活,只有对象不可触及的时候才可以被回收

判断对象是否可以回收

  • 如果对象到GC root没有引用连接,则进行一次标记
  • 进行筛选,判断对象是否执行finalize方法,如果没有重写finalize方法中和已经被虚拟机调用,被判断为不可触及的;如果对象重写了finalize方法且还未执行过,则会被插入F-Queue队列中,由一个虚拟机自动床下你的低优先级的Finalizer线程出发其finalize方法执行,finalize方法as对象逃亡的最后机会,稍后GC会对队列中的对象进行第二次标记

垃圾清除阶段算法

目前在JVm中比较常见的三种垃圾收集算法是标记清除算法,复制算法,标记压缩算法

  1. 标记清除:当堆中有效空间被耗尽的时候就STW,然后进行两项工作,标记和清除
  • 标记:collector 从引用根节点开始遍历,标记所有被引用的对象,一般是在对象的header中记录为可达对象(标记的非清除对象)
  • 清除: collector 对堆内存从头到尾进行线性遍历,如果某个对象在其header中没有标记为可达对象,则将其回收,这里的清除是把需要清除的对象地址保存在空闲列表中,下次有新的对象需要加载的时候,判断垃圾位置的空间是否足够,够就存放。
  1. 复制算法:将活着的内存空间分为两块,每次只使用一块,在垃圾回收的时候将正在使用的内存的活着的对象复制到未使用的内存块中, 之后清除正在使用的内存块中的所有对象,交换内存的角色完成垃圾回收。
  2. 标记压缩算法:第一阶段和标记清除算法一样,从根节点开始标记所有被引用的对象,第二阶段将所有的存活对象压缩到内存的一端,按照顺序排放,之后清理边界外所有的空间。
  3. 增量收集算法:如果一次性将所有的垃圾进程处理, 需要造成系统长时间的停顿,所以可以让垃圾收集线程和应用程序线程交替执行,每次垃圾回收线程只收集一小部分区域空间,接着切换到应用线程,以此反复直到完成。这样减少了系统的停顿时间,但是线程上下文切换,会使得垃圾回收的总体成本上升,造成系统的吞吐量下降
  4. 分区算法: 分区算法按照对象的生命周期长短划分为2个部分,分区算法将整个堆划分为连续的不同小空间,每个小空间独立使用独立回收,可以一次控制多个小区。
posted @ 2022-04-04 21:45  Henry19  阅读(27)  评论(0编辑  收藏  举报