JAVA垃圾回收

垃圾对象分析:

  1. 引用计数法:
    1. 每个对象添加一个引用计数器,每次被引用,计数器+1,失去引用,计数器-1,计数器为0,就应该对象可以被回收了;
    2. 没法解决循环引用的问题;
  2. 可达性分析法:
    1. GC Root节点开始,遍历整条引用链,寻找存活节点,回收剩余的对象;
    2. 可以作为GC Root节点的对象有:
      1. 虚拟机栈中的reference对象
      2. 本地方法栈中的引用对象
      3. 方法区中静态变量引用对象
      4. 方法区中常量引用对象

引用的分类:

  1. 强引用:
    1. 只要强引用存在,就不会回收;
  2. 软引用
    1. 内存不足时,会回收软引用;
  3. 弱引用
    1. 下一次GC时,就会被回收;
  4. 虚引用
    1. 即时失效;

垃圾回收算法:

  1. 标记清除算法
    1. 主要分为两步:
      1. 第一步,标记出所有需要清除的对象;
      2. 第二步,统一回收所有标记的对象;
    2. 缺点:
      1. 标记与清除的效率都比较低;
      2. 标记清除过后会产生大量不连续的内存碎片,当大对象需要分配内存时,可能又会触发一次GC;
  2. 复制算法
    1. 将内存一分为二,先使用其中一块,其中一块用完了,再将存活的对象找出来,复制到另外一块,然后清理掉之前一块
    2. 新生代大多采用复制算法来实现;
      1. 新生代被分为EdenSurvior,其中Survior区又被分为FromTo,其比例默认是Eden:From:To = 8 :1 :1
      2. 每次先使用Eden,Eden满了,复制到From,然后清空EdenFrom也满了,将EdenFrom的存活对象复制到To,然后清空EdenFrom,再将FromTo互换,一直保持To是空的;
  3. 标记整理算法
    1. 标记整理算法多用于老年代,就是在标记清除的基础上加上内存整理
  4. 分代算法
    1. 根据对象的声明周期将内存划为多块,JAVA将内存划为新生代老年代
    2. 新生代与老年代的比例默认是 1 :2
    3. 新生代发生的是monitorGC,老年代发生的FullGC
    4. 每次monitorGC,新生代对象年龄+1,年龄达到阀值就会进入老年代
    5. MonitorGC发生前会先进行分配担保,如果分配担保失败就会进行FullGC
      1. 分配担保是为了保证monitorGC安全性,会去判断老年代的最大可用空间是否大于新生代的使用空间,如果小于,就意味着这次有可能会发生FullGC

GC优劣标准:

  1. 吞吐量:GC线程会与应用线程竞争CPU时间,吞吐量指的是应用线程使用CPU时间的比例吞吐量越高越好
  2. 停顿时间:GC程序为了保证在回收过程中不会产生新的垃圾,达到一定的一致性,会在GC时让应用线程停顿一段时间,停顿时间越短越好

垃圾回收器:

https://i-blog.csdnimg.cn/blog_migrate/5485d9517bf3788a7aa0e17b5985ccf7.png

  1. Serial收集器
    1. 新生代单线程串行收集器,采用复制算法,会在GC时Safepoint暂停所有用户线程;
  2. ParNew收集器
    1. Serial多线程版本,单核CPU表现不如Serial;
  3. Parallel Scavenge收集器
    1. 追求最大吞吐量,和ParNew一样是多线程复制算法收集器;
    2. 停顿时间短适合用户交互类的程序;
    3. 吞吐量大适合后台运算不需要太多交互的程序;
    4. 停顿时间新生代空间吞吐量三者不能同时满足
  4. Serial Old收集器
    1. Serial收集器的老年代版本,采用标记整理算法
  5. Parallel Old收集器
    1. 老年代多线程标记整理
  6. CMS收集器
    1. 最短停顿时间为目标;
    2. 采用标记清除算法
    3. 步骤:
      1. 初始标记:仅标记出于GCRoot节点直接关联的节点,速度很快,需要STW
      2. 并发标记:对GCRoot整条链路进行标记,时间最久不需要STW
      3. 重新标记:修正并发标记期间,用户程序的修改导致的不一致的对象,时间比初始标记略久,需要STW
      4. 并发清除:不需要STW;
    4. 缺点:
      1. CPU资源敏感:由于CMS是并发与用户线程一起工作,所以在CPU核数不足时,吞吐量会变的非常低;
      2. 无法清理浮动垃圾:在并发清除时不会STW,用户线程还会持续产生垃圾
      3. 会产生内存碎片:CMS是标记清除算法,没有整理的过程;
  7. G1收集器
    1. 将JAVA堆分为对多个region
    2. 新生代与老年代不再物理隔离,而是散落在不同的region上;
    3. 可预测的时间模型:G1根据历史回收时间在每个region上加上了价值权值,可以根据权值来优先回收价值最大的region
    4. 避免全堆扫描:每个region维护了一个Remembered Set,记录了与Reference数据的关系,可达性分析时,合并Remembered Set,就可以遍历出GCRoots的整条链路;
    5. 步骤:
      1. 初始标记
      2. 并发标记
      3. 最终标记:合并并发标记阶段的Remembered Set log到Remembered Set中;
      4. 筛选回收:根据Region中的价值权值进行排序,根据用户期望停顿时间来选择回收部分Region

基础概念:

  1. SafePoint
    1. GC线程工作的过程中,由于用户线程也在工作,可能会导致引用关系的变化,所以GC发生时需要在某一个点,让所有的用户线程都停下来;
    2. 停下的方式有两种:
      1. 抢先式中断:GC发生时,立即中断所有用户线程,再去检查线程是否在SafePoint上,如果不在,就继续运行到SafePoint再中断;(这种方式很少有虚拟机用)
      2. 主动式中断:GC程序会在SafePoint设置标识,用户线程读到标识为真时,就把自己中断挂起;
    3. 可作为SafePoint的点:
      1. 循环体的结尾;
      2. 方法返回前;
      3. 调用call方法后;
      4. 抛出异常的地方;
  2. Safe Region
    1. 用户线程如果没有CPU时间片,就没法跑到SafePoint,GC程序这种线程所在的代码标记为Safe Region,线程想进入到RUNNING,就要先检查GC是否结束,没结束的话,就不能从Safe Region出去;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值