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

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

1765126

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

 

今天想跟大家聊聊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有深入研究。分享的文章偏硬核,很硬的那种。不考虑交个朋友吗?关注硬核子牙:

 

分类:
标签:
请先登录,再评论

请教一下:JVM在生成执行流代码的时候,都会在适合作为安全点的地方插入一段代码。执行流是业务代码吧?适合的地方的依据是什么?

2月前
回复 HIP:

是否有长时间运行的可能。如果有,安全点生效后,用户线程迟迟无法暂停,GC就无法发生,就需要插安全点,比如for循环。触发安全点后,后面就是循环判断是否所有线程进入了安全点,进入就减1……知道等于0,GC就可以工作了

2月前回复

为你推荐

不起眼,但是足以让你有收获的JVM内存分析案例
分析 这个问题说白了,就是说有些int[]对象不知道是哪里来的,于是我拿他的例子跑了跑,好像还真有这么回事。点该 dump 文件详情,查看相关的 int[] 数组,点该对象的“被引用对象”,发现所
从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
协助美团kafka团队定位到的一个JVM Crash问题
概述 有挺长一段时间没写技术文章了,正好这两天美团kafka团队有位小伙伴加了我微信,然后咨询了一个JVM crash的问题,大家对crash的问题都比较无奈,因为没有现场,信息量不多,碰到这类问题我
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
JVM实战:优化我的IDEA GC
IDEA是个好东西,可以说是地球上最好的Java开发工具,但是偶尔也会卡顿,仔细想想IDEA也是Java开发的,会不会和GC有关,于是就有了接下来对IDEA的GC进行调优 IDEA默认JVM参数: -
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得