笔记——gc回收

​ 1.引用计数法(对象中添加一个引用计数器)

​ 2.可达性分析法

GC ROOTS

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象

**不可达的对象也要至少经历两次标记,对象进行可达性分析后与GC roots的引用链无关,就会标记一次,然后判断是否有必要执行finalize()方法进行第二次标记判断。**假如对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,那么这两种就被视为没有必要执行。

对象有必要执行finalize()方法就被放置在一个F-Queue的队列中,并稍后去执行,这里的执行是会触发方法开始运行,但不一定承诺会等待它结束

finalize()方法只会被系统调用一次

垃圾收集算法

1、标记-清除算法(Mark-Sweep)

“标记-清除”算法是最基础的算法,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。它主要由两个缺点:一个是效率问题,标记和清除过程的效率都不高;另一个是碎片化

2、标记-复制算法(Copying)(针对新生代)

​ 为了解决标记清除算法的效率问题,出现了复制算法,它将可用内存按容量划分为大小相等的两块,每次使用其中的一块。当这块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。优点是每次都是对其中的一块进行内存回收,内存分配时就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。缺点是将内存缩小为原来的一半,代价太高了一点。

Appel式回收将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保。即如果另外一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。

3、标记-整理算法(Mark-Compact)(针对老年代)

​ 根据老年代的特点提出了“标记-整理”算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

标记-整理的步骤:

  1. 标记阶段
  2. 整理阶段:移动存活对象,同时更新存活对象中所有指向被移动对象的指针

4、分代收集算法(Generational Collection)

​ 一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

安全点

  1. 方法返回之前

  2. 调用某个方法之后

  3. 抛出异常的位置

  4. 循环的末尾

记忆集与卡表

记录集:记录从非收集区到收集区的指针集合(解决跨代引用)

**卡表:**实现记忆集最常用的一种方式

卡表是使用一个字节数组实现

在这里插入图片描述

一个卡页中可包含多个对象,只要有一个对象的字段存在跨代指针,其对应的卡表的元素标识就变成1,表示该元素变脏,否则为0.

GC时,只要筛选卡表中变脏的元素加入GCRoots。

Stop The World

暂停所有的工作线程,直到它收集完成

写屏障

使用写屏障维护卡表状态

垃圾收集器

Serial (新生代)

1.单线程(收集期间需要暂停其他所有线程)

2.客户端模式下的默认收集器

优点:

1.简单高效(与其他收集器的单线程相比)

2.内存资源受限时,额外内存消耗最小

3.因为没有线程交互的开销,因此可获得最高的单线程效率

在这里插入图片描述

ParNew(新生代)

  1. 多线程并行收集(用户进程需等待)

  2. 目前唯一一个能与CMS收集器配合工作

    使用-XX:+UseConcMarkSweepGC参数,使用ParNew+CMS+Serial Old收集器组合,此时ParNew是新生代的默认收集器

  3. 默认开启的收集线程数与处理器核心数相同。

    可以使用-XX:ParallelGCThreads参数限制线程数

在这里插入图片描述

Parallel Scanvenge(新生代)

1.基于标记-复制

2.并行收集

3.关注吞吐量 用户代码时间/(用户代码时间+垃圾收集时间)

适合在后台运算而不需要太多交互的任务

Parallel Old(老年代)

  1. 多线程并发收集
  2. 基于标记整理
  3. 注重吞吐量(Parallel Scanvenge + Parallel Old)

Serial Old(老年代)

CMS(老年代)

关注停顿时间—> B/S等服务端模式中给用户给拱良好的交互体验

运作过程

**1.初始标记 (Initial Mark)**标记与GCRoots直接关联的对象,速度快

**2.并发标记(Concurrent Mark)**从GCRoots直接关联的对象开始遍历整个对象图,不暂停用户线程,可与GC线程并发运行,耗时长

**3.重新标记(Remark)**修正并发标记期间因用户线程导致标记产生变动的对象

**4.并发清除(Concurrent Sweep)**删除掉被标记死亡的对象,可与用户程序并发运行

在这里插入图片描述

缺点

  1. 占用了一部分线程
  2. 无法处理浮动垃圾
  3. 空间碎片过多

Garbage First(G1)

基于Region的内存布局

Region还有一类Humongous区域,专门存储大对象(只要超过一个region容量一半的对象即可判定为大对象)

运行过程

  1. 初始标记

  2. 并发标记

  3. 最终标记

  4. 筛选回收

    • 更新region的统计数据
    • 对所有region按照回收价值和成本排序
    • 根据用户设定的停顿时间制定回收计划
    • 自由选择多个region作为回收集,将决定回收的region的存活对象复制到另一个空的region,再清理整个旧region
    • 暂停用户进程,可多条收集器并行执行。

    更新->排序->制定回收计划->选择回收集->复制清理(并行、STW)

    除了并发标记,都需要停止用户进程。

img

优点:

  1. 延迟可控情况下获得尽可能高的吞吐量。

  2. 用户可指定期望(要符合实际)的停顿时间

    默认为200ms,不能太低,太低会造成每次选择的回收集只占堆的很小一部分,回收速度跟不上分配速度,导致Full GC

  3. 分区(region)管理堆内存

  4. 按回收效益确定回收集

  5. 整体基于标记-整理,局部基于标记-复制,不会产生碎片

缺点(与CMS比)

  1. 内存占用

    因为每个region都需要维护一个卡表解决跨代引用问题,记忆集可能占整堆的20%以上。CMS则只有一份卡表。

  2. 额外执行负载高

    和CMS一样都使用写屏障维护卡表。

    除了和CMS一样使用写后屏障外(直接使用同步),G1还需要写前屏障实现原始快照算法,产生额外负担。(使用消息队列)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值