JVM 垃圾回收 | 青训营笔记

51 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第5天

阅读来源: javaguide.cn/java/jvm/jv…

众所周知,java中堆内存是主要用来管理对象分配和回收的空间,在没有垃圾回收机制的语言中,需要手动调用free函数来释放空间。优秀的垃圾回收机制能帮助我们免除这样的烦恼。

堆结构

e8680b2980f64c2bb96998539484d275_tplv-k3u1fbpfcp-watermark.png

Survivor区这里是笔误

内存分配和回收原则

在伊甸区首次分配

对象在新生代中 Eden 区分配。
当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。

空间分配担保

空间分配担保是为了确保在 Minor GC 之前老年代本身还有容纳新生代所有对象的剩余空间。 只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行 Minor GC,否则将进行 Full GC。

stop the world

blog.csdn.net/kep159/arti…
指的是用户线程在运行至安全点(safe point)或安全区域(safe region)之后,就自行挂起,进入暂停状态,对外的表现看起来就像是全世界都停止运转了一样,而不论何种gc算法,不论是minor gc还是major gc都会stop the world,区别只在于stop the world的时间长短。

Minor GC(新生代GC)

  • 触发时机:Eden区无法为新对象分配空间的时候
  • 操作区域:eden-->Survivor区 , Survivor区-->Survivor区 , Survivor区-->老年代 ,直接进老年代
  • 是否需要STW:是
  • 作用过程:blog.csdn.net/weixin_4261…
      1. 对eden区域做可达性分析,存活的对象被复制到S0,不存活的对象被回收, 并且存活的对象年龄都增大一岁
      1. 当Eden 和 s0区空间满了,S0的所有的数据都被复制到S1,需要注意的是,在上次minor GC过程中移动到S0中的两个对象在复制到S1后其年龄要加1。此时Eden区S0区被清空,所有存活的数据都复制到了S1区,并且S1区存在着年龄不一样的对象
      1. 再下一次MinorGC则重复这个过程,这一次survivor的两个区对换,存活的对象被复制到S0,存活的对象年龄加1,Eden区和另一个survivor区被清空。
      1. 再经过几次Minor GC之后,当存活对象的年龄达到一个阈值之后(默认是15),就会被从年轻代Promotion到老年代
      1. 大对象直接进入老年代

Major GC/Full GC(全局GC)

  • 触发时机:
    • 1、每次晋升的对象的平均大小 > 老年代剩余空间
    • 2、Minor GC后存活的对象超过了老年代剩余空间
    • 3、System.gc()有可能会触发
    • 4、元空间不足
  • 操作区域:针对整个新生代、老生代、元空间的全局范围的GC。
  • 是否需要STW:是

判断对象死亡算法

引用计数法

给对象中添加一个引用计数器:

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效,计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的。

循环引用时,两个都无效,但都不为0

可达性分析算法

“GC Roots”  的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。

GC Roots

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
public class Test {
    public static Test s;
    public static  void main(String[] args) {
	Test a = new Test();
	a.s = new Test();
        
        //a引用被断开,对象被回收
	a = null;
    
        //s 在此时是类静态属性引用,s就是GC Root
        Sout(s);
    }
}
  • 本地方法栈(Native 方法)中引用的对象

  • 所有被同步锁持有的对象

对象可以被回收,就代表一定会被回收吗?

finalize执行后再结束

垃圾收集算法

标记-清除算法

首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。

  1. 效率问题
  2. 空间问题(标记清除后会产生大量不连续的碎片)

标记-复制算法

为了解决效率问题,“标记-复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

image.png

标记-整理算法

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

image.png

垃圾收集器

并行和并发概念补充:

  • 并行(Parallel)  :指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
    • Parallel Scavenge 收集器 --用户线程阻塞
  • 并发(Concurrent) :指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个 CPU 上。
    • cms收集器 --用户线程不阻塞

CMS 收集器

使用的是最差的有碎片的标记清除算法

6c637411236f40ab92cc97475ecbb48b_tplv-k3u1fbpfcp-watermark.png

G1 收集器

标记复制算法