【JVM】SafePoint与各类垃圾收集器工作过程

2024-02-23 11:08

本文主要是介绍【JVM】SafePoint与各类垃圾收集器工作过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • SafePoint安全点与Stop The World
  • 垃圾回收器
    • 1. Serial收集器(复制算法)
    • 2. ParNew收集器(复制算法)
    • 3. Parallel Scavenge收集器(复制算法)
    • 4. Serial Old收集器(标记-整理算法)
    • 5. Parallel Old收集器(标记-整理算法)
    • 6. CMS收集器(标记-清除算法)
    • 7. G1收集器
    • 总结

SafePoint安全点与Stop The World

在介绍各个垃圾回收器之前,先了解SafePoint的概念。

  • 在进行对象可达性分析过程中,工作必须在一个能确保一致性的快照中进行——这里一致性的意思是指整个分析期间整个执行系列看起来就像被冻结在某个时间点上,不可以出现分析过程中对象引用关系还在不断变化的情况。
    • 这就导致了GC进行时必须停顿所有的java执行线程(Stop The World)
  • 当系统停顿下来之后,并不需要一个不漏地检查完所有的执行上下文和全局引用位置,虚拟机通过一组成为OopMap的数据结构,直接得知哪些地方存放着对象引用。
    • 在类加载完成的时候,Hotspot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中也会在特定的位置记录下栈和寄存器中哪些位置是引用。
    • 但是如果为每一条指令都生成对应的OopMap,那么将需要大量的额外空间。前面也提到了,是在“特定位置”上记录了这些信息,这些位置就叫做SafePoint安全点。程序执行时并非在所有的位置都可以停顿下来开始GC,只有在到达安全点时才能暂停。
  1. 如何在GC发生时让所有的线程都跑到最近的安全点上再停顿下来?
    有两种方式:

    • 抢先式中断:在GC发生时,先把所有的线程中断,如果发现有线程中断的地方不在安全点上,就恢复线程让它跑到安全点再停下。
    • 主动式中断:当GC需要中断线程时,不直接对线程进行操作,而是设置一个标志,各个线程在执行到安全点时,去主动轮询这个标志,如果发现需要暂停,就自己中断挂起。大部分虚拟机都是使用的这种方式。
  2. 什么地方可以放SafePoint?
    理论上来讲,每一条指令都要一个SafePoint,但是为了减少性能消耗,要尽可能的减少SafePoint的数量。
    通过JIT编译的代码里,在所有的方法返回之前,或者所有的无界循环回跳之前,放一个SafePoint,以防止发生stw时该线程不能暂停。
    比如for(int 1=0;i<100;i++)这种有界循环是没有SafePoint的。

  3. SafePoint的作用?
    SafePoint机制可以Stop The World,保证一致性的工作环境,不仅仅是在GC的时候用,有很多其他地方也会用它来Stop The World,阻塞所有Java线程,从而可以安全地进行一些操作。

  4. 安全域是什么?
    SafePoint保障了程序执行时,在不太长时间内就会遇到可进入GC的安全点,但是程序不执行的时候呢?典型的例子就是线程处于Sleep或者Blocked状态,无法响应JVM的中断请求。
    这时就需要safeRegion安全域来解决,安全域是指在一段代码中,引用关系不会发生变化,在这个区域中任何地方开始GC都是安全的。

  5. 我们知道SafePoint可以实现Stop The World,那么除了GC之外其他全局进入safePoint可能有哪些情况?

    • 由于 jstack,jmap 和 jstat 等命令,也就是 Signal Dispatcher 线程要处理的大部分命令,都会导致 Stop the world:这种命令都需要采集堆栈信息,所以需要所有线程进入 Safepoint 并暂停。
    • Java Instrument 导致的 Agent 加载以及类的重定义:由于涉及到类重定义,需要修改栈上和这个类相关的信息,所以需要 Stop the world
    • Java Code Cache相关:当发生 JIT 编译优化或者去优化,需要 OSR 或者 Bailout 或者清理代码缓存的时候,由于需要读取线程执行的方法以及改变线程执行的方法,所以需要 Stop the world
    • GC:这个由于需要每个线程的对象使用信息,以及回收一些对象,释放某些堆内存或者直接内存,所以需要 Stop the world
  6. Stop The World耗时过长可能的原因?
    Stop the world 阶段可以简单分为:

    • 某个操作,需要 Stop the world
    • 向 Signal Dispatcher 这个 JVM 守护线程发起 Safepoint 同步信号并交给对应的模块执行。
    • 对应的模块,采集所有线程信息,并对每个线程根据状态做不同的操作以及标记
    • 所有线程都进入 Safepoint 并 block。
    • 做需要发起 Stop the world 的操作。
    • 操作完成,所有线程从 Safepoint 恢复。
      基于这些阶段,导致 Stop the world 时间过长的原因有:
      • 阶段 4 耗时过长,即等待所有线程中的某些线程进入 Safepoint 的时间过长,这个很可能和有大有界循环 与 JIT优化有关,也很可能是 OpenJDK 11 引入的获取调用堆栈的类StackWalker的使用导致的,也可能是系统 CPU 资源问题或者是系统内存脏页过多或者发生 swap 导致的。
      • 阶段 5 耗时过长,需要看看是哪些操作导致的,例如偏向锁撤销过多, GC时间过长等等,需要想办法减少这些操作消耗的时间,或者直接关闭这些事件(例如关闭偏向锁,关闭 JFR 的 OldObjectSample 事件采集)减少进入,这个和本篇内容无关,这里不赘述。
      • 阶段2,阶段3耗时过长,由于 Signal Dispatcher 是单线程的,可以看看当时 Signal Dispatcher 这个线程在干什么,可能是 Signal Dispatcher 做其他操作导致的。也可能是系统 CPU 资源问题或者是系统内存脏页过多或者发生 swap 导致的。

垃圾回收器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
在这里插入图片描述

  • 新生代可配置的回收器:Serial、ParNew、Parallel Scavenge
  • 老年代配置的回收器:CMS、Serial Old、Parallel Old
  • 新生代和老年代区域的回收器之间进行连线,说明他们之间可以搭配使用。
  • G1是一个独立的收集器不依赖其他6种收集器

1. Serial收集器(复制算法)

Serial是一个单线程的回收器,在进行垃圾回收时,必须暂停其他所有的工作线程。
在这里插入图片描述

2. ParNew收集器(复制算法)

ParNew其实就是Serail的多线程版本,使用了多条线程进行垃圾收集。
在这里插入图片描述

3. Parallel Scavenge收集器(复制算法)

Parallel Scavenge也是一个多线程的、使用复制算法的收集器,与ParNew不同的是它的目标是达到一个可控制的吞吐量。

  • 吞吐量 = 运行用户代码时间 /(运行用户代码时间+垃圾收集时间)
    停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验;
    高吞吐量可以高效利用CPU时间,尽快地完成程序运算任务,主要适合在后台运算而不需要太多交互的任务。

4. Serial Old收集器(标记-整理算法)

Serial Old是Serial的老年代版本,同样是一个单线程收集器。这个收集器的主要意义在于给Client模式下的虚拟机使用。

5. Parallel Old收集器(标记-整理算法)

Parallel Old是Parallel Scavenge的老年代版本,在注重吞吐量以及CPU资源敏感的场合,都可以优选考虑Parallel Scavenge+Parallel Old。

6. CMS收集器(标记-清除算法)

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其注重服务的响应速度,希望系统停顿时间最短,以给用户带来好的体验。
整个过程分为4个步骤:

  1. 初始标记:标记一下GC Roots能直接关联到的对象,速度很快
  2. 并发标记:进行GC Roots Tracing的过程
  3. 重新标记:修正并发标记期间用户线程继续运行导致标记变动的那部分对象记录
  4. 并发清除
    在这里插入图片描述

其中初始标记和重新标记仍然需要Stop The World。单总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
它存在3个明显的缺点:

  • 对CPU资源非常敏感。CMS的并发能力依赖于CPU资源,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致程序变慢,总吞吐量降低。
  • 无法处理浮动垃圾:由于CMS的并发清理阶段用户线程还在运行,在这一阶段出现的垃圾,将无法当次收集中处理掉它们,只好等下一次再回收。
  • 收集结束时有大量空间碎片,由其使用的标记-清除算法导致。

7. G1收集器

G1(Garbage First)收集器是当今收集器技术发展的最前沿成果之一。它是一款面向服务端应用的垃圾收集器,具备如下特点:

  • 并行与并发:G1可以充分利用多CPU多核环境的硬件优势,使用多个CPU来缩短STW停顿的时间
  • 分代收集:虽然G1不需要其他收集器就可以独立管理整个GC堆,但它能够采用不同的方式去处理新建的对象和已经存活了一段时间、熬过多次GC的就对象,以获取更好的收集效果。
  • 空间整合:G1整体来看是标记-整理算法,从局部来看是基于复制算法实现的。但无论如何都意味着不会产生内存碎片。
  • 可预测的停顿:这是G1相对于CMS的另一优势,G1除了追求低停顿外,还可以建立可预测的停顿时间模型。

使用G1收集器时,java堆内存的分布与其他收集器有很大差别,它将整个java堆划分为多个大小相等的独立区域Region

  • 虽然还保留着新生代和老年代的概念,但它们之间不再是物理隔离的,而是一部分Region的集合。
    在这里插入图片描述

如何避免全堆扫描?

  • 虽然它把堆分为了多个Region,但是Region之间并不可能是孤立的,一个对象分配在某个Region中,并非只能被本Region中的对象引用,而是可以与整个堆中的对象发生引用关系。那么在判定对象是否存活的时候,岂不是要扫描整个堆?
    这个问题只是在G1中更加突出,以前的分代收集中,新生代与老年代收集时也面临同样的问题。
    • 在G1收集中,Region之间的对象引用以及其他收集器中新生代老年代之间的对象引用,虚拟机都是通过Remembered Set来避免全堆扫描的。
    • G1中每一个Region都有一个与之对应的Remembered Set,如果虚拟机发现了在对引用类型的数据写操作时,引用对象处于不同的Region,那么将相关信息记录到Remembered Set。当进行内存回收时,在GC Roots的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

G1收集器的整体过程(不包括Remembered Set的操作):

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收
    在这里插入图片描述
    它的前几个步骤与CMS很像,最后再筛选回收阶段,首先对各个Region的回收价值与成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,时间是用户可控的。

总结

在这里插入图片描述

这篇关于【JVM】SafePoint与各类垃圾收集器工作过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/738426

相关文章

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.