JVM经典垃圾回收器的运行机制和原理

文章详细阐述了JVM的内存区域划分,包括方法区、堆、虚拟机栈、本地方法栈和程序计数器,并讲解了对象的内存布局。接着介绍了两种判断对象是否需要被回收的算法:引用计数法和可达性分析,并对比了增量更新和原始快照两种并发标记策略。此外,文章还讨论了内存分代模型,包括串行、并行和CMS收集器,以及G1收集器的特色和工作流程。

1 JVM运行时内存划分

1.1 五大内存区域

jvm-memory
  • 方法区

    属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    运行时常量池,属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用。

    JDK1.8之前,Hotspot虚拟机对方法区的实现叫做永久代,1.8之后改为元空间。二者区别主要在于永久代是在JVM虚拟机中分配内存,而元空间则是在本地内存中分配的。很多类是在运行期间加载的,它们所占用的空间完全不可控,所以改为使用本地内存,避免对JVM内存的影响。

    根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。

  • 线程共享,主要是存放对象实例和数组。

    如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

    PS:实际上写入时并不完全共享,JVM会为线程在堆上划分一块专属的分配缓冲区来提高对象分配效率。详见:TLAB

  • 虚拟机栈

    线程私有,方法执行的过程就是一个个栈帧从入栈到出栈的过程。

    每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

    如果线程入栈的栈帧超过限制就会抛出StackOverFlowError,如果支持动态扩展,那么扩展时申请内存失败则抛出OutOfMemoryError

  • 本地方法栈

    和虚拟机栈的功能类似,区别是作用于Native方法。

  • 程序计数器

    线程私有,记录着当前线程所执行的字节码的行号。其作用主要是多线程场景下,记录线程中指令的执行位置。以便被挂起的线程再次被激活时,CPU能从其挂起前执行的位置继续执行。

    唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

    注意:如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地址。如果为native(底层方法),那么计数器为空。

1.2 对象的内存布局

在 HotSpot 虚拟机中,对象分为如下3块区域:

  • 对象头(Header)

    运行时数据:哈希码、GC分代年龄、锁状态标志、偏向线程ID、偏向时间戳等。

    类型指针:对象的类型元数据的指针,如果对象是数据,还会记录数组长度。

  • 对象实例数据(Instance Data)

    包含对象真正的内容,即其包括父类所有字段的值。

  • 对齐填充(Padding)

    对象大小必须是是8字节的整数倍,所以对象大小不满足这个条件时,需要用对齐填充来补齐。

2 标记的方法和流程

2.1 判断对象是否需要被回收

要分辨一个对象是否可以被回收,有两种方式:引用计数法可达性算法

  • 引用计数法

    就是在对象被引用时,计数加1,引用断开时,计数减1。那么一个对象的引用计数为0时,说明这个对象可以被清除。

    这个算法的问题在于,如果A对象引用B的同时,B对象也引用A,即循环引用,那么虽然双方的引用计数都不为0,但如果仅仅被对方引用实际上没有存在的价值,应该被GC掉。

  • 可达性算法

    通过引用计数法的缺陷可以看出,从被引用一方去判定其是否应该被清理过于片面,所以我们可以通过相反的方向去定位对象的存活价值:一个存活对象引用的所有对象都是不应该被清除的(Java中软引用或弱引用在GC时有不同判定表现,不在此深究)。这些查找起点被称为GC Root

2.2 哪些对象可以作为GC Root呢?

  1. JAVA虚拟机栈中的本地变量引用对象

  2. 方法区中静态变量引用的对象

  3. 方法区中常量引用的对象

  4. 本地方法栈中JNI引用的对象

3.3 GC时如何快速找到GC Root呢?

Hotspot把所有GC Root对象都存放在OopMap中,当GC发生时,直接从这个map中寻找GC Root。

将GC Root存放到OopMap有两个触发时间点:

  1. 类加载完成后,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来
  2. 即时编译过程中,也会在特定的位置记录下栈里和寄存器里哪些位置是引用

3.4 并发可达性分析

三色标记法

白色:表示垃圾回收过程中,尚未被垃圾收集器访问过的对象,在可达性分析开始阶段,所有对象都是白色的,即不可达

黑色:被垃圾收集器访问过的对象,且这个对象所有的引用均扫描过。黑色的对象是安全存活的,如果其他对象被访问时发现其引用了黑色对象,该黑色对象也不会再被扫描。

灰色:被垃圾收集器访问过的对象,但这个对象至少有一个引用的对象没有被扫描过。

那么标记阶段就是从GC Root的开始,沿着其引用链将每一个对象从白色标记为灰色最后标记为黑色的过程。

标记过程中不一致问题

由于这个阶段是层层递进的标记,所以过程中难免出现不一致的情况导致原本是黑色的对象被标记为白色,比如,当前扫描到B对象了,C对象尚未被访问时,标记情况如下:

GC Root
A
B
C

那么如果这时A对象取消了对B对象的引用,而GC Root增加了对C对象的引用,GC Root作为黑色标记不会再次被扫描,那么C对象在标记阶段结束后仍然会保持白色,就会被清除掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值