JVM笔记之垃圾回收

极客时间 JVM

JVM常见垃圾回收算法

引用计数法

古老,基础

  1. 原理:统计每个对象被引用次数,如果为0,就释放对象,回收无用内存

问题:

  1. 并发场景下,引用计数的修稿和对象指针的修改必须是同步的,所以需要加锁or无锁算法
  2. 有时会引发连锁式回收(等待时间无法预测)
  3. 循环引用的问题(致命问题)

思考:

  1. 如何解决循环引用
  2. 是否可以先减去旧obj的引用计数,再增加新obj引用计数?
    我觉得不可以,因为减去为0了然后就直接回收了,然后再给他加就来不及了

基于拷贝的算法

原理:将堆一分为二,一半为from,一半为to,用from进行分配,空间不足的时候,触发gc,gc把存活对象复制到to,然后把from和to互换

优点

  1. 采用bump the pointer(碰撞指针javascript:void(0);法),要分配多少空间就移多少次指针,高效;而回收是否高效需要取决于存活对象的比例

缺点

  1. 浪费一半内存空间
  2. 需要停顿(业务线程)

Mark-Sweep

使用链表管理所有的区域,存活的就表示,不存活的就把内存free

基于拷贝的垃圾算法(重点)

问题:对象位置发生变化时,指向它的引用如何维护?(说人话:ab都引用c,ac搬到新空间了,b怎么知道c的新地址)

  1. 引入中间层:一个间接指针,但虽然复制简单了,但分配和回收不容易做

  2. forwarding 指针:它从c指向c’,b找到c后就可以通过forwarding指针来找到c’

  3. 改进:提高空间的利用率:Eden,Survivor0,Survivor1;这样浪费的空间只有Survivor0/1一个。

图算法(其实没搞清楚JVM和它的关系)

  1. 广度优先
    前一层没有遍历完,就不会访问到后一层的节点,所以BFS适合求解最优化的问题
    JDK6.0之前用BFS
  2. 深度优先
    JDK6.0之后DFS,主要是因为用了DFS,有利于cpu的缓存命中
  3. DFS应用:八皇后,走迷宫
总结:copy-base适合存活对象少的情况,mark-sweep适合存活对象多的情况。
对于生命周期不同的对象,会希望使用不同的算法。因此,将堆分为了新生代和老年代,新生代使用scavenge(就是s0,s1,eden);老年代使用mark-sweep

并发垃圾回收

分代垃圾回收算法

  1. 新生代:对象的创建在新生代的Eden空间
  2. 晋升:新生代进入老年代,此时它可能会成为年轻代GC的root GC,为了避免每次找root都需要对老年代遍历一遍,就引入card table来标记老年代对象中包含的年轻代的指针
  3. 根引用:不在堆中,但指向堆的引用。它包括栈上的引用,全局变量,JVM的Handler等
  4. 维护跨代引用:card Table,解决对象增多时记录集维护的复杂性
    下图是记录集

    下图是card table,使用的是位图,是用来维护跨代引用的,所以不分代就不会有card table;将512bytes映射为一个byte,当byte置位,则表示这512bytes的堆空间包含指向年轻代的引用;如果一个对象大于512bytes,就会让两个标志位的card全部置位

GC停顿和并发标记算法

  1. GC停顿:拷贝算法中对象的地址会发生移动,
  2. Mark-Sweep因为不涉及地址的转移,因此可以做到没有GC停顿,这就是并发的GC算法

三色标记算法

不是真实存在的?是个抽象的算法?它只是一个抽象的概念,用于代表不同的状态
白色:未完成搜索
灰色:正在搜索
黑色:已经搜索完成

问题:DFS的时候灰色的状态是什么

  1. 并发标记的问题:
    发生在黑色对向白色对象的引用(漏标),因为b是正在扩展的引用,如果它不能到达c,c就会漏标

解法:

  1. 直接把白色标灰,不会影响效率(往前进
  2. 把黑色对象标灰,减少浮动垃圾(往后退
  3. B对C的引用删除的时候,将C标记为灰色,这时候即使不再被引用,也会被判定为存活。这样就会产生一些存活着的垃圾
  4. cms 本质上也是往后退,但充分利用了card table;这时它就有两个功能:维护跨代引用,标记灰色节点

链式思考:

  1. 并发标记为什么会产生漏标:因为并发的时候会有多个线程对对象的引用关系进行更改,如果更改的时候恰好出现黑色对象直接引用白色对象,就会发生漏标
  2. 为了防止漏标,引入了两种barrier(前进后退
  3. cms为了实现防止漏标方法,就使用card table来重新标记
  4. 在真正使用的时候,会担心young gc时候会破坏掉card table的数据结构,所以将card table转移到 mod union table

CMS参数调优

  1. metaSpace和maxMataSpace最好设置成一样的,因为mateSpace满了就一定会fullGC

分块GC,从分代进化而来的

所有对象都集中在一个区域的话,gc停顿事件时不可控的,所以分块gc产生了

新的挑战:

  1. 维护跨区引用
  2. 合理选择回收区域
  3. 考虑分代的影响

write barrier

更改对象之间引用关系的时候,可以通过一些额外的事情来维护相关的信息。引用技术和card table都是一种写屏障

G1 GC

分为Minor GC和Mixed GC;前者只回收年轻代,后者回收年轻代和部分老年代;
G1 的full GC是把系统听下来,整个GC做回收

GC参数

posted @ 2021-09-05 22:56  concise_d  阅读(72)  评论(0)    收藏  举报