GC终结标记 SuspendEE 是怎么回事
一:背景
1. 讲故事
写这篇是起源于训练营里有位朋友提到了一个问题,在 !t -special
输出中有一个 SuspendEE
字样,这个字样在 coreclr 中怎么弄的?输出如下:
0:000> !t -special
ThreadCount: 3
UnstartedThread: 0
BackgroundThread: 2
PendingThread: 0
DeadThread: 0
Hosted Runtime: noLock DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception0 1 4ab0 000001CC44E5C490 2a020 Cooperative 0000000000000000:0000000000000000 000001cc44e520d0 -00001 MTA (GC) 11 2 19d8 000001CC44E84700 21220 Preemptive 0000000000000000:0000000000000000 000001cc44e520d0 -00001 Ukn (Finalizer) 12 3 6668 000001CC44ED4520 2b220 Preemptive 0000000000000000:0000000000000000 000001cc44e520d0 -00001 MTA OSID Special thread type0 4ab0 SuspendEE 10 3b6c DbgHelper 11 19d8 Finalizer
哈哈,其实我特别能理解,很多人学了高级调试之后好奇心会爆棚,看啥都想探究底层,有一种技术上的重生,这篇我们就好好聊一聊。
二:WinDbg 分析
1. SuspendEE 标记是什么
这个单词全称为 Suspend Engine Execution, 即 冻结执行引擎
,那冻结执行引擎的入口方法在哪里呢?这个考验着你对GC运作骨架图的认识,在 coreclr 源码中有一个骨架图,简化后如下:
GarbageCollectGeneration(){SuspendEE();garbage_collect();RestartEE();}garbage_collect(){generation_to_condemn();gc1();}
上面的 SuspendEE()
即 SOS 中的 SuspendEE 标记的入口函数,接下来我们深入探究下这个方法。
2. SuspendEE 到底做了什么
如果你仔细阅读过 SuspendEE() 方法的源代码,你会发现核心枚举变量是 ThreadType_DynamicSuspendEE
,它起到了定乾坤的作用,参考代码如下:
thread_local size_t t_ThreadType;void ThreadSuspend::SuspendEE(SUSPEND_REASON reason)
{// set tls flags for compat with SOSClrFlsSetThreadType(ThreadType_DynamicSuspendEE);
}void ClrFlsSetThreadType(TlsThreadTypeFlag flag)
{t_ThreadType |= flag;gCurrentThreadInfo.m_EETlsData = (void**)&t_ThreadType - TlsIdx_ThreadType;
}enum PredefinedTlsSlots
{TlsIdx_ThreadType = 11 // bit flags to indicate special thread's type
};enum TlsThreadTypeFlag // flag used for thread type in Tls data
{ThreadType_DynamicSuspendEE = 0x00000020,
}
从上面的代码中可以看到 t_ThreadType 是一个 C++ 级的线程本地存储,意味着每一个线程都有其备份,同时它也是 SuspendEE 标记的核心来源,如果 m_EETlsData 的第 11号 槽位为 0x20 的时候, SuspendEE 标记就会被成功打下,并且可以通过 gCurrentThreadInfo.m_EETlsData 变量去跟踪来源,有了这么多信息之后,接下来就可以代码验证了。
三:案例验证
1. 一段测试代码
代码非常简单,就是一个简单的手工 GC触发。
internal class Program{static void Main(string[] args){Debugger.Break();GC.Collect();Console.ReadLine();}}
接下来使用 windbg 在入口的 SuspendEE 方法上下断点 bp coreclr!ThreadSuspend::SuspendEE
观察,截图如下:
一旦将 ThreadType_DynamicSuspendEE=0x20
赋值之后,接下来用 windbg 去做个验证。
0:000> x coreclr!*gCurrentThreadInfo*
000001a1`668ee8c0 coreclr!gCurrentThreadInfo = struct ThreadLocalInfo0:000> dx -id 0,0 -r1 (*((coreclr!ThreadLocalInfo *)0x1a1668ee8c0))
(*((coreclr!ThreadLocalInfo *)0x1a1668ee8c0)) [Type: ThreadLocalInfo][+0x000] m_pThread : 0x1a166902e50 [Type: Thread *][+0x008] m_pAppDomain : 0x1a166948b40 [Type: AppDomain *][+0x010] m_EETlsData : 0x1a1668ee880 [Type: void * *]0:000> dp 0x1a1668ee880
000001a1`668ee880 00000000`00000000 00000000`00000000
000001a1`668ee890 00000000`00000000 00000000`00000000
000001a1`668ee8a0 00000000`00000000 00000000`00000000
000001a1`668ee8b0 00000000`00000000 00000000`00000000
000001a1`668ee8c0 000001a1`66902e50 000001a1`66948b40
000001a1`668ee8d0 000001a1`668ee880 00000000`00000020
从上面输出可以看到 000001a1668ee8d0+0x8
地址的内容已经被成功种下,相信这时候 !t -special
也能拿到标记了。
0:000> !t -specialLock DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception0 1 640c 000001A166902E50 2a020 Preemptive 000001A16B0094A8:000001A16B00A5B8 000001a166948b40 -00001 MTA (GC) 11 2 3e50 000001A16692B2D0 21220 Preemptive 0000000000000000:0000000000000000 000001a166948b40 -00001 Ukn (Finalizer) 12 3 6a24 000001A16699F8F0 2b220 Preemptive 0000000000000000:0000000000000000 000001a166948b40 -00001 MTA OSID Special thread type0 640c SuspendEE 10 76b0 DbgHelper 11 3e50 Finalizer
那这个 0x20 什么时候被拿掉呢? 这个在源码中也能找到相应的答案,继续 go 运行,输出如下:
void ClrFlsClearThreadType(TlsThreadTypeFlag flag)
{t_ThreadType &= ~flag;
}0:012> dp 0x1a1668ee880
000001a1`668ee880 00000000`00000000 00000000`00000000
000001a1`668ee890 00000000`00000000 00000000`00000000
000001a1`668ee8a0 00000000`00000000 00000000`00000000
000001a1`668ee8b0 00000000`00000000 00000000`00000000
000001a1`668ee8c0 000001a1`66902e50 000001a1`66948b40
000001a1`668ee8d0 000001a1`668ee880 00000000`00000000
当然如果你去寻找 sos 的源码实现,也会找到相应的答案。
HRESULT PrintSpecialThreads()
{...if (ThreadType & ThreadType_DynamicSuspendEE){type += "SuspendEE ";}...return Status;
}
四:总结
挖掘这个标记的前世今生回头看其实还是挺有意思的,coreclr 居然新增了 m_EETlsData 字段来给 sos 做妥协,哈哈,这彰显了 sos 一等公民的地位。
相关文章:

GC终结标记 SuspendEE 是怎么回事
一:背景 1. 讲故事 写这篇是起源于训练营里有位朋友提到了一个问题,在 !t -special 输出中有一个 SuspendEE 字样,这个字样在 coreclr 中怎么弄的?输出如下: 0:000> !t -special ThreadCount: 3 UnstartedTh…...

Ubuntu 中GCC交叉编译工具链安装
Ubuntu 自带的 gcc 编译器是针对 X86 架构的,如果要编译的是 ARM 架构的代码,就需要一个在 X86 架构的 PC 上运行,可以编译 ARM 架 构代码的 GCC 编译器,这个编译器就叫做交叉编译器,总结一下交叉编译器就是&#x…...

JEXL(Java Expression Language)用法概览
JEXL(Java Expression Language)是一个用于在Java应用程序中解析和执行表达式的库。JEXL的设计目的是通过提供一种类似于脚本语言的语法,使得可以在应用程序中动态地计算表达式的值。JEXL常用于模板引擎、规则引擎和配置文件等场景。 下面介…...

NC 完全二叉树结点数
系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 描述 给定一棵完全…...

点灯案例优化(二) 利用位运算修改特定位
前面,我们对点灯代码进行了第一次优化,效果如下 尽管第一次优化以后代码可读性确实高了不少,也看起来更加简洁,但是,这里仍旧存在一个很严重的问题:就在每一个表达式右边,我们给寄存器的数据赋值…...

【C++备忘录】
记录一些C比较好用的代码块,方便自个查看。 使用std::copy 快速打印序列 #include <iostream> #include <algorithm> #include <iterator>int main() {int a[5] { 1, 2, 3, 4, 5 };copy(begin(a), end(a), ostream_iterator<int>(cout, …...

java编程 斐波拉契数列算法集锦【斐波拉契数列】【下】【集合类】【Stream函数式编程】
斐波那契数列(Fibonacci sequence),又称黄金分割数列,是一个非常经典的递归问题。斐波那契数列的算法描述: 斐波那契数列,一个令人着迷而又充满神秘色彩的数字序列,它以0和1作为起始ÿ…...

智慧园区三维可视化平台
背景 随着物联网、人工智能等新一代信息技术的发展,数字孪生技术逐渐成为实现这一目标的关键工具。数字孪生技术能够对物理世界进行高精度、全要素的映射,并实时动态反映其变化情况,从而为园区提供精准的管理和服务。 方案简介 智慧园区数字…...

Redis 有序集合【实现排行榜】
使用 Redis 的 Sorted Set 数据结构可以非常高效地实现实时排行榜功能。Sorted Set 允许将元素按分数进行排序,同时支持插入、删除和查询操作,且这些操作的时间复杂度较低,非常适合处理高并发的场景。 实现思路 插入操作:当用户…...

ORACLE数据库管理系统介绍
1.ORACLE的特点: 可移植性 ORACLE采用C语言开发而成,故产品与硬件和操作系统具有很强的独立性。从大型机到微机上都可运行ORACLE的产品。可在UNIX、DOS、Windows等操作系统上运行。可兼容性 由于采用了国际标准的数据查询语言SQL,与IBM的SQL/DS、DB2等均兼容。并提供读取其它…...

C# 中Linq探讨 Or条件拼接
在C#中,没有直接内置于.NET Core或.NET Framework中的NuGet包能够直接“拼接”LINQ的OR条件,因为LINQ本身设计为一种声明式编程模型,用于查询数据集合。然而,你可以通过一些方式来实现多个条件以OR逻辑组合的效果,而不…...

有关应用层面试题有关库的思维导体
面试题目: TCP通信中3次握手和四次挥手? 答: 第一次握手:客户端发送SYN包(SYN1, seq0)给服务器,并进入SYN_SENT状态,等待服务器返回确认包。第二次握手:服务器接收到S…...

记一次 SAP BP 编号范围错误引发的一个问题 GET_NRIV_LINE
本来想着循着错误提示去排查,但是还是想看看业务发生了什么,他们的操作是否有问题,不经意间发现 号码段是有问题的,由此大概可以判断是他们编号范围和类型之间的问题 角色和分组是否一致的,如果不一致就发生了以上错误…...

(17)ELK大型储存库的搭建
前言: els是大型数据储存体系,类似于一种分片式存储方式。elasticsearch有强大的查询功能,基于java开发的工具,结合logstash收集工具,收集数据。kibana图形化展示数据,可以很好在大量的消息中准确的找到符…...

每日一问:Kafka消息丢失与堆积问题分析(简化版)
Kafka 消息系统问题解析 在本篇博客中,我们将深入探讨 Kafka 中常见的两大问题:消息丢失和消息堆积。首先,我们将简要介绍 Kafka 的基本工作原理,随后分别分析消息丢失和堆积的原因,并提供针对性的解决方案。 关于其详…...

C语言中函数sizeof和strlen区别
sizeof和strlen是C语言中的两个常用函数,它们的作用和使用方式有所不同。 sizeof sizeof是一个运算符而非函数,用于计算数据类型或变量占用的字节数。它可以计算任意数据类型(包括基本类型、自定义结构体、数组等)的大小。例如&…...

RAG与LLM原理及实践(14)---- Python + MinIO + Kafka进阶
目录 背景 根因分析 配置 构造 创建 network 构造 zookeeper 构造 kafka 参数构造 原理解析 图解 全过程解析 工具使用 kafkacat 查看 broker python 实现 python send + kafka recv python 代码 kafka recv 运行效果 python recv + kafka send python 代…...

接口自动化-代码实现
接口自动化基础 1、接口自动化测试 接口自动化:使用工具或代码代替人对接口进行测试的技术测试目的: 防止开发修改代码时引入新的问题测试时机: 开发进行系统测试转测前,可以先进行接口自动化脚本的编写开发进行系统测试转测后&…...

如何查看linux大文件
文章目录 一、查看存储情况二、查看指定路径下的文件大小查看临时文件和日志的大小 三、查找home目录下文件大小大于100M的大文件四、查看INNODE使用情况五、查看进程使用情况查看所有进程查看特定进程杀死相关进程 六、清除缓存操作七、 查看docker的硬盘占用情况详细查看 一、…...

生成式人工智能服务大模型备案答疑
问:大模型备案范围 答:利用生成式人工智能技术向中华人民共和国境内公众提供生成文本、图片、音频、视频等内容的服务,适用本办法。 未向境内公众提供生成式人工智能服务的,不适用本办法的规定。 ps:生成式人工智能…...

QT-贪吃蛇小游戏
QT-贪吃蛇小游戏 一、演示效果二、核心代码三、下载链接 一、演示效果 二、核心代码 #include "Food.h" #include <QTime> #include <time.h> #include "Snake.h"Food::Food(int foodSize):foodSize(foodSize) {coordinate.x -1;coordinate.…...

虚幻5|AI视力系统,听力系统,预测系统(1)视力系统
继宠物伴随系统初步篇后续 虚幻5|AI巡逻宠物伴随及定点巡逻—初步篇-CSDN博客 一,听力系统 1.打开宠物ai的角色蓝图 2.选中ai感知组件 右侧细节,找到ai感知,添加感知配置,我们需要的是ai视力配置 3.选中左侧创建的ai感知组件&…...

IC rankIC
IC IC衡量的是预测值和实际值之间的相关系数 计算公式为:IC Pearson(R(predicted),R(actual)) 取值范围:[-1, 1],其中1表示完全相关,也就是预测值和实际值完全一样。0表示完全不相关,-1表示,反向相关 ra…...

Windows服务器IIS7下如何查看真实报错原因
背景 IIS7默认为友好报错,或只报错代码。如500错误,401错误等。根据这些错误无法定位真实原因,故而需要显示真实的错误信息。 解决方案 以500错误为例说明。 1、打开IIS,点全局设置中的"错误页"(注意必须是全局网站)。 2、右击50…...

深度学习设计模式之策略模式
文章目录 前言一、介绍二、特点三、详细介绍1.核心组成2.代码示例3.优缺点优点缺点 4.使用场景 总结 前言 策略模式定义一系列算法,封装每个算法,并使它们可以互换。 一、介绍 策略模式(Strategy Pattern)是一种行为型设计模式&…...

Linux 下安装miniconda(少走弯路)
Miniconda 和 Conda 都是用于管理 Python(及其他语言)环境和包的工具。 conda对于我来说是太臃肿了,很多的包我不会使用,所以选择安装miniconda是一个较好的选择。 下面是linux安装miniconda的实际操作。 在以下的网站…...

java ssl使用自定义证书
1.证书错误 Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 2.生成客户端证书 openssl x509 -in <(openssl s_client -connect 192.168.11.19:8101 -prexit 2>/dev/null) -ou…...

【ARM+Codesys 客户案例 】基于RK3568/A40i/STM32+CODESYS开发的控制器在自动输送分拣系统上的应用,支持定制
2021年“京东618” 累计下单金额超3438亿元,再次刷新纪录! 从下单到收货,各种货品均可在短短几天内通过四通八达的物流网络送达全国任何一个家庭。电子商务和快递物流的迅猛发展对仓储、分拣、配送效率和准确性均提出了更高的要求,加速了智能物流的发展。…...

C++ 设计模式(1. 单例模式)
单例模式是一种创建型设计模式, 它的核心思想是保证一个类只有一个实例,并提供一个全局访问点来访问这个实例。 特点 全局访问点的意思是,为了让其他类能够获取到这个唯一实例,该类提供了一个全局访问点(通常是一个静态…...

算法笔记|Day31动态规划IV
算法笔记|Day31动态规划IV ☆☆☆☆☆leetcode 1049.最后一块石头的重量II题目分析代码 ☆☆☆☆☆leetcode 494.目标和题目分析代码 ☆☆☆☆☆leetcode 474.一和零题目分析代码 ☆☆☆☆☆leetcode 1049.最后一块石头的重量II 题目链接:leetcode 1049.最后一块石…...