性能文章>我就奇了怪了,STW到底是怎么做到的>

我就奇了怪了,STW到底是怎么做到的原创

3094428

哈喽,大家好,我是江湖人送外号[道格牙]的子牙老师。

 

今天想跟大家聊聊STW,它是怎么给程序世界按下暂停键的

 

问题分析

 

STW,即Stop The World。

 

为什么需要STW呢?试想你妈给你打扫房间的场景:把你撵出去,关上门,打扫干净,打开门,数落你,揍你…一套标准化流程后,房间干净了。打完你,***心情变好了,打麻将都能多赢点。

 

这里面有个关键环节:把你撵出去。尽管在打扫房间的过程中你可能不会制造垃圾,但是你的存在就有这个风险,所以必须把你撵出去。这话不是我说的,是从***行为中揣摩出来的。^_^

 

试想,如果不把你撵出去,你妈打扫垃圾的同时,你又陆陆续续制造了垃圾,那这场打扫房间的行动是不是变成了无法结束的行动啊。或者到某个时间点,你妈打扫了一半走了,丢下一句话:朽木不可雕也,孺子不可教也。

 

垃圾收集器也是一样的,为了保证清理垃圾的完整性,在某些环节,就会STW。比如所有垃圾收集器中都有的一个阶段:初始阶段,即扫描根对象,需要STW。小伙伴门看过的几乎所有资料,讲到这基本就没了对吧。但这不是子牙老师我的风格,咱们接着往后面说。

 

STW

 

JVM中要做到STW是很难的。为什么这么说呢?因为需要考虑很多很多因素。

 

一、JVM中存在多种类型的会发生改变内存行为的线程:

  1. 执行业务逻辑的用户线程

  2. 执行native方法的Java线程

  3. 执行垃圾收集的GC线程

  4. 执行即时编译的JIT线程

 

二、每种类型的线程个数,在需要STW的那一刻,可能都不止一个。

 

三、每种类型的线程,在需要STW的那一刻,执行到的代码位置也未可知。

 

四、每种类型的线程阻塞的点还不能随机。因为线程在阻塞前需要更新OopMap。

 

OopMap是什么?你可以理解成是记录这个线程一路跑下来经历过的所有Java对象的集合。为什么要有OopMap呢?因为没有的话,你就得扫描整个栈,去查找根对象。

 

这里说的只是查找根对象的一种情况哈,勿抬杆,我会记仇。^_^

 

如何暂停线程

 

听我这么一分析,好像确实很复杂哈。那如果是你来实现,你会怎么解决呢?小伙伴门可以想一想。经常想这样有深度的问题,有利于提高你的思考深度。

 

我们还是来看看JVM是如何高明地解决的吧。

 

如果线程随便哪个位置阻塞都合适,这个问题就会简单一百倍。但是这里简单了,给其他地方就带来了灾难。就是说线程阻塞前需要更新OopMap,如果不更新,没有这个数据的话,GC时就需要扫描所有线程的所有栈的所有栈帧来查找根对象。

 

OopMap的存在,其实又是一种空间换时间的策略。因为相比内存的价格,降低GC延时明显更重要。

 

但是JVM的执行流那么多,何时?在什么地方?更新OopMap呢?这就是安全点存在的意义。安全点同时解决了STW及更新OopMap。

 

其实也可以这样说,不理解安全点就无法理解STW,甚至于无法理解GC。

 

安全点

 

安全点涉及的知识点非常多、非常底层。本篇文章就讲安全点中与STW相关的知识点。其他的知识点后面会写系列文章展开讲。感兴趣的小伙伴可以关注我公众号关注我的发文动态:硬核子牙。

 

这段代码是大家看GC源码时经常看到的

SafepointSynchronize::begin

我把hotspot源码中核心的代码粘过来

 

这段代码到底做了哪些事情呢:

  1. 告诉JVM马上要开始GC(下雨)了,开始做准备工作了(准备收衣服了)。本质就是修改一些属性位。比如第5行代码,通知解释器做好准备工作,迎接GC到来。

  2. 将polling_page对应的物理页设置成不可读状态。这步非常非常重要。等下说。

  3. 不停检测,确定是否所有的线程都已进入安全点。只有都已进入安全点,才能执行GC逻辑。

 

STW的真面目

 

安全点是如何解决让所有的线程都阻塞的呢?开启安全点为什么要将物理页的属性改为不可读呢?

 

因为JVM在生成执行流代码的时候,都会在适合作为安全点的地方插入一段代码

 

 

这段代码就是安全点的本质,也是触发STW的本质。什么意思呢?如果os::_polling_page对应的物理页属性是可读的,这段代码并没什么特殊意义。但是如果是不可读的,读的时候就会触发段异常,对应的操作系统信号:SIGSEGV。

 

JVM捕获了这个异常,并进行了处理。所有的线程都是在这个地方STW的。

 

 

这就是安全点难的地方,涉及到的知识点太多太底层!其实我搞手写JVM小班的核心目的不是带你写一个JVM,其一是让你通过手写JVM了解hotspot的体系,你才能看得懂hotspot源码。其二,也是最核心的,掌握底层。因为掌握了底层,你对技术就没有恐惧之心了,你会觉得你无所不能。事实上,相对的无所不能是可以做到的,只是需要时间沉淀。啰嗦了两句哈。

 

GC结束后唤醒所有阻塞的线程,小伙伴们应该能想到是在哪里?如何唤醒的了吧

 

 

推荐阅读

 

1、搭建JVM框架,输出hello,world

2、超快速定位OOM一揽子计划

3、JVM的多态是如何实现的

4、从hotspot源码层面剖析Java的多态实现原理

 

 

 

你好,我是子牙。十余年技术生涯,一路披荆斩棘从小白到技术总监到大厂中间件到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核及特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。不考虑交个朋友吗?关注硬核子牙:

 

点赞收藏
分类:标签:
子牙_公号硬核子牙

对编程语言的设计与实现有浓厚兴趣。聚焦Hotspot源码、Linux内核研究,硬核干货分享

请先登录,查看4条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

万字完整深入解析JVM面试必备,原来这就是和年薪百万的差距

万字完整深入解析JVM面试必备,原来这就是和年薪百万的差距

搭稳Netty开发的地基,用漫画帮你分清同步异步阻塞非阻塞

搭稳Netty开发的地基,用漫画帮你分清同步异步阻塞非阻塞

为什么 JVM 叫做基于栈的 RISC 虚拟机

为什么 JVM 叫做基于栈的 RISC 虚拟机

如何随心所欲调试HotSpot VM源代码?(改造为CMakeLists项目)

如何随心所欲调试HotSpot VM源代码?(改造为CMakeLists项目)

朋友们,就在今天,JDK 21,它终于带着重磅新特性正式发布了!!!

朋友们,就在今天,JDK 21,它终于带着重磅新特性正式发布了!!!

13 轻量级锁的重入 以及 线程1获取轻量级锁并释放线程2获取锁 的调试

13 轻量级锁的重入 以及 线程1获取轻量级锁并释放线程2获取锁 的调试

28
4