zoukankan      html  css  js  c++  java
  • Java中的垃圾回收

    垃圾回收

    确定对象是否存活?

    两种确定对象是否存活:

    • 引用计数算法
    • 可达性分析算法

    引用计数算法

    给对象中添加引用计数器,若有一个引用,则计数值加一,若引用失效,计数值减一。
    存在的问题:难以解决循环引用问题。

    可达性分析算法

    通过GC Roots作为起始节点,根据引用关系向下开始搜索,所有搜索走过的路径称为“引用链”。如果一个对象没有到达GC Roots的引用链,则为不可达,可以认为该对象可以被回收。

    GC Roots对象

    • 虚拟机栈中引用的对象(方法中使用参数,局部变量,临时变量)
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象(字符串常量池中的引用)
    • 本地方法栈中引用的对象
    • 虚拟机内部的引用(基本数据类型对象的Class对象)
    • synchronized持有的对象。

    引用

    分类

    • 强引用 :引用不会被垃圾收集器回收。通过 new 创建的引用。
    • 软引用 :在内存不足时,对于这些进行回收。通过 SoftReference 创建软引用。
    • 弱引用 :对象存活到下一次垃圾收集。通过 WeakReference 创建弱引用。
    • 虚引用 :不影响对象的生存周期,无法通过虚引用获得对象实例。用于在对象被回收时收到系统通知。

    回收方法区

    主要是回收:

    • 废弃的 常量
    • 不再使用的 类型 

    判断一个类是否可以被回收的条件

    • 该类的所有实例对象已被回收。(堆中不存在该类及其子类的实例)
    • 加载该类的类加载器已被回收。
    • 对应的Class对象没有被引用。

    注:大量使用 反射 , 动态代理 的场景中,需要有类型卸载的能力。


    垃圾收集算法

    Java堆的划分:

    • 新生代:对象创建的区域,每次垃圾收集会回收大量的对象。
    • 老年代:保存着生存周期较长的对象。

    垃圾收集分类:

    • Minor GC/Young GC:目标为新生代的垃圾收集。
    • Major GC/Old GC:目标是老年代的垃圾收集。(CMS)
    • Mixed GC:收集整个新生代和部分老年代的垃圾收集(G1)

    三种主要的垃圾收集算法

    • 标记-清除算法
    • 标记-复制算法
    • 标记-整理算法

    标记-清除算法

    分为两个阶段:

    • 标记处所有 需要回收 的对象。
    • 回收所有被标记的对象。

    存在的问题

    • 被回收的对象很多时,效率较低。
    • 会导致内存碎片,内存利用率低。

    标记-复制算法

    流程:

    将内存划分成两块,每次只使用其中一块。当这一块用完时,将存活的对象复制到另外一块中,然后回收这一块。
    优化:将新生代分为:

    • Eden区
    • From Survivor
    • To Survivor

    Eden : Survivor = 8 : 2
    每次使用Eden区和其中一个Survivor区。当内存不足时,将存活的对象复制到另外一个Survivor区,然后将Eden区和Survivor区进行回收。(Survivor区不足容纳存活的对象时,会使用老年区)

    不足

    每次使用内存的一部分,利用率低。

    标记-整理算法

    不会产生空间碎片。

    流程:

    • 标记所有可以回收的对象。
    • 将所有存活的对象向内存空间一端移动。
    • 直接清理边界以外的内存。

    不足

    移动对象带来的引用的更新导致整体效率较低,必须暂停用户线程才能执行。
    关注吞吐量 --> 标记-整理算法
    关注延迟 --> 标记-清除算法


    垃圾收集器

    • 新生代:
      • Serial收集器
      • ParNew收集器
      • Parallel Scavenge收集器
    • 老年代:
      • Serial Old 收集器(MSC)
      • Parallel Old收集器
      • CMS收集器
    • 整个堆:G1收集器

    Serial收集器

    • 单线程
    • 回收新生代
    • 采用标记-复制算法回收
    • 暂停用户线程(Stop The World)
    • 客户端默认

    ParNew收集器

    • 多线程并行收集
    • 回收新生代
    • 需要暂停所有用户线程
    • 采用标记-复制算法
    • 服务端默认

    Parallel Scavenge收集器

    • 回收新生代
    • 多线程并行收集
    • 基于标记-复制算法
    • 暂停用户线程
    • 目标:达到可控的吞吐量(用户代码运行时间与处理器总消耗时间之比),高效率完成垃圾回收任务,适用于后台运算不需要太多交互的任务。

    Serial Old收集器

    • 回收老年代
    • 单线程收集
    • 基于 标记-整理算法
    • 暂停所有用户线程

    Parallel Old收集器

    • 回收老年代
    • 多线程并发收集
    • 采用 标记-整理算法
    • 暂停用户线程
    • 吞吐量优先的垃圾收集器,可与Parallel Scavenge搭配使用。

    CMS收集器

    • 以获得最短停顿时间为目标,重视响应速度
    • 回收老年代
    • 采用 标记-清除算法
    • 分为四个阶段
      • 初始标记
      • 并发标记
      • 重新标记
      • 并发清除

    流程:

    • 初始标记:需要暂停用户线程,使用一个线程标记GC Roots能直接关联的对象。
    • 并发标记:单个线程,与用户线程并发执行,标记所有可可达对象。
    • 重新标记:需要暂停用户线程,多线程并发执行,用来修正由于用户线程执行导致的标记变动。
    • 并发清除:与用户线程并发执行,使用一个线程清除所有不可达的对象。

    不足:

    • 对于CPU资源敏感,占用部分线程,导致响应变慢。
    • 无法处理浮动垃圾。由于GC线程与用户线程并发执行,对于新产生的垃圾无法回收,所以必须预留空间给用户线程。
    • 采用 标记-清除算法 ,可能导致内存碎片。

    G1收集器

    特点:

    • 回收的范围是整个新生代和老年代
    • 将Java堆分成大小相等的区域(Region)
    • 通过对每个Region计算其回收价值(获得的空间和花费的时间),在后台维护一个优先队列,根据允许的收集时间,优先回收价值最大的Region。

    流程:

    • 初始标记
    • 并发标记
    • 最终标记
    • 筛选回收:暂停用户线程,多线程并发回收。
      • 首先对每个Region进行按照回收价值和成本进行排序。
      • 再根据用户的期望停顿时间制定回收计划。
  • 相关阅读:
    一条select语句的执行流程
    理解数据库的事物,ACID,cap
    java并发volatile和sychnorized的底层机制
    避免死锁的几种方式
    如何减少线程上下文切换
    RestTemplate设置超时时间
    spring事务隔离级别和传播级别
    mysql数据库与其他数据库的区别
    spingcloud组件注解汇总
    python二级选择题易错知识点总结
  • 原文地址:https://www.cnblogs.com/truestoriesavici01/p/13662901.html
Copyright © 2011-2022 走看看