前言

在现代的容器化和微服务应用中,因为分布式的环境和错综复杂的调用关系,APM(Application Performance Monitoring 应用性能监控)显得尤为重要,它通过采集应用程序各种指标和请求链路,让你知道系统当前的状态和值得优化的点,另外能帮助你发现应用程序的异常,帮助你更方便的定位问题。

对于.NET这样带GC(Garbage Collector 垃圾回收器)的平台来说,GC的指标也尤为重要,采集可以帮助我们分析内存泄漏、优化系统性能等等。在公司内部已经可以采集比较全面的.NET GC指标,如下图所示。

在绝大多数场景它能满足要求,但是如果遇到某时某刻P95延时突然增大,异步任务突然超时,我们想排查这些异常是否因为GC的STW Time(Stop The World Time 指GC运行过程中所有线程被挂起的时间)过长导致的,就没有办法了,因为目前没有采集这些指标。

所以本文就带大家了解一下,如何采集.NET GC STW Time。

方法

如.NET内存性能分析指南中提到的一样,.NET Runtime在运行过程中会发布很多事件,这些事件代表了当前Runtime的运行状态,同样GC在运行过程中也会发布很多事件,我们可以使用 PerfView 工具来收集这样的一些事件。下面是 WorkStationGC 发生GC时的一个事件序列。

Microsoft-Windows-DotNETRuntime/GC/SuspendEEStart  //开始暂停托管线程运行
Microsoft-Windows-DotNETRuntime/GC/SuspendEEStop    //暂停托管线程完成
Microsoft-Windows-DotNETRuntime/GC/Start    // GC开始回收
Microsoft-Windows-DotNETRuntime/GC/Stop     // GC回收结束
Microsoft-Windows-DotNETRuntime/GC/RestartEEStart   //恢复之前暂停的托管线程
Microsoft-Windows-DotNETRuntime/GC/RestartEEStop    //恢复托管线程运行完成

PS: 所有的事件都可以在 .NET文档官方 中找到,非常的全面。

而 SuspendEEStart(暂停托管线程运行) 到 RestartEEStop(恢复托管线程运行完成) 中经过的时间就是STW Time,我们只需要记录这两个事件的差值,就可以知道本次GC STW的时间有多长。

BGC的过程比WorkStationGC复杂的很多,但是一样是测量这两个事件花费的时间来采集STW Time,本文不做过多介绍。

使用EventSource采集

那么我们知道通过计算哪两个指标的差值来获得STW时间,那么应该如何通过代码来采集呢?

这里就需要知道 EventSource 和 EventListener 两个类,顾名思义我们可以通过 EventSource 来发布事件,使用 EventListener 来监听事件,在本文中我们也主要使用 EventListener 来收集GC事件,对于这 EventSource 类的使用大家可以看下面给出的微软文档链接,这里不做过多介绍。

  • EventSource
  • EventListener
    我们来看一看如何使用 EventListener 类监听GC事件,代码如下所示:
using System.Diagnostics.Tracing;  // 开启GC事件监听
var gc = new GcStwMetricsCollector();
// 创建一些对象
var array = Enumerable.Range(0, 1000).Select(s => (decimal)s).ToArray();
// 手动执行GC
GC.Collect();
Console.ReadLine();  public class GcStwMetricsCollector : EventListener
{  // GC关键字  private const int GC_KEYWORD = 0x0000001;  // 我们要关注的GC事件  private const int GCSuspendEEBegin = 9;  private const int GCRestartEEEnd = 3;  private EventSource? _eventSource;  public void Stop()  {  if (_eventSource == null)  return;  DisableEvents(_eventSource);  }  protected override void OnEventSourceCreated(EventSource eventSource)  {  _eventSource = eventSource;  // GC 事件在 Microsoft-Windows-DotNETRuntime 名称空间下
if (eventSource.Name.Equals("Microsoft-Windows-DotNETRuntime"))  {  // 启用事件,事件级别为Informational, 只监听GC事件  EnableEvents(eventSource, EventLevel.Informational, (EventKeywords) (GC_KEYWORD));  }  }  private long _currentStwStartTime = 0;  protected override void OnEventWritten(EventWrittenEventArgs e)  {  switch (e.EventId)  {  // 冻结托管线程开始,记录当前时间  case GCSuspendEEBegin:  _currentStwStartTime = e.TimeStamp.Ticks;  break;  // 恢复托管线程结束,计算当前时间与冻结托管线程开始时间的差值  case GCRestartEEEnd:  if (_currentStwStartTime > 0)  {  var ms = TimeSpan.FromTicks(e.TimeStamp.Ticks - _currentStwStartTime).TotalMilliseconds;  _currentStwStartTime = 0;  // 输出结果  Console.WriteLine($"STW: {ms}ms");  }  break;  }  }
}

运行结果:

STW: 0.2568ms

至于GC事件对应的枚举值,大家可以在我上文中给出的文档中找到。

.NET7新API

在实现这个需求时,我注意到.NET7有一个新的 issue ,直接提供了一个API,让我们可以获取到总的GC STW Time,我把重点的信息摘抄和翻译了一下。

背景和动机

今天我们已经在 GetGCMemoryInfo 公开了获取GC处理时间和暂停时间的百分比值的API。

具体来说是通过 GCMemoryInfo 的 PauseTimePercentage 字段。

这个很有用,但是如果我只想要一个分子(即:程序运行以来总的GC暂停时间)。现在没有办法获取到。

API 提案

我建议在 System.GC 上添加一个下面这样的API:

TimeSpan System.GC.GetTotalPauseDuration()

它会返回GC总的暂停时间。

API 使用

TimeSpan start = System.GC.GetTotalPauseDuration();
// ... Perform some work ...
TimeSpan end= System.GC.GetTotalPauseDuration();
Console.WriteLine(end - start + " was spent pausing in GC");

我看到这个API已经和最新的.NET7预览版一起发布,我们下载最新的.NET7 SDK,然后把项目改成.NET7,来试试这个API,代码如下所示:

using System.Diagnostics.Tracing;  // 开启GC事件监听
var gc = new GcStwMetricsCollector();
// 创建一些对象
var array = Enumerable.Range(0, 1000).Select(s => (decimal)s).ToArray();
// 手动执行GC
GC.Collect();
Console.WriteLine($"API STW:{GC.GetTotalPauseDuration().TotalMilliseconds}ms");
Console.ReadLine();
// 省略上文中一样的代码

运行结果:

API STW: 0.223ms
Event STW: 0.296ms

API统计的应该会更加准确,我们通过事件来获取多多少少有一点额外的开销,不过误差在可接受的范围内。

总结

上文中提到了两种方式来获取.NET GC STW Time,我们只需要稍加改造,就可以将STW监控的功能加入APM中,如下图表就是本地测试时采集的一些数据。

当然通过 EventListener 还可以实现更多的APM信息的采集,大家有兴趣也可以研究看看。

本文代码链接Github: BlogCodes/Get-GC-STW-Time at main · InCerryGit/BlogCodes · GitHub

如何获取GC(垃圾回收器)的STW(暂停)时间?相关推荐

  1. Garbage First (G1) GC垃圾回收器:区域化分代式【图文】

    1.问:既然我们已经有了前面几个强大的GC,为什么还要发布Garbage First (G1) GC? 答:(1)原因就在于应用程序所应对的业务越来越庞大.复杂,用户越来越多,没有GC就不能保证应用程 ...

  2. Java -GC 垃圾回收器

    GC 垃圾回收器: 简介:GC 垃圾回收器是 JVM 中自动内存管理机制的具体实现,在 HotSpot 虚拟机中 GC 的工作主要划分为两 大类,分别是内存动态分配和垃圾回收,在内存执行分配之前,GC ...

  3. java常见的gc回收器_一篇文章让你了解GC垃圾回收器

    简单了解GC垃圾回收器 了解GC之前我们首先要了解GC是要做什么的?顾名思义回收垃圾,什么是垃圾呢? GC回收的垃圾主要指的是回收堆内存中的垃圾对象. 从根对象出发,所有被引用的对象,都是存活对象 其 ...

  4. [Java基础]-- Java GC 垃圾回收器的分类和优缺点

    关于Java的垃圾回收器,一直是个头疼的问题,这里简要说明下分类和优缺点,供选择使用. 一.JVM GC 垃圾回收器类型 JVM的垃圾回收器大致分为六种类型: 1.串行:垃圾回收器 (Serial G ...

  5. 面向对象,类,对象,GC垃圾回收器,private关键字

    一.面向对象 是一种编程思想 1.三大特征 封装.继承.多态 二.类 class 类是一类事物的抽象 属性 --事物的特征 方法 --事物的行为 三.对象 是一类事物中具体的个体 对象类型 对象名 = ...

  6. Java GC 垃圾回收器

    一.垃圾定位 没有引用指向的对象被称为垃圾.如何定位垃圾?一般有两种算法,引用计数法 Reference Count,和根可达算法 Root Searching. 1.引用计数法 在对象上标记被指向的 ...

  7. JAVA垃圾回收器源码_浅谈关于Java的GC垃圾回收器的一些基本概念

    一.基本回收算法 1. 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此 ...

  8. JVM—GC垃圾回收器总结

    引言   如果说收集算法(标记-清理.复制.标记-整理.分代收集)是内存回收的方法论,那毫无疑问,垃圾收集器就是内存回收的具体实现.   主要有7个gc器,如下图: Serial收集器 介绍   Se ...

  9. 【JVM · GC】垃圾回收器

    1. GC分类与性能指标 1.1 垃圾回收期器概述 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于 JDK 版本处于高速迭代过程中,因此Java 发展至今已经衍 ...

最新文章

  1. 关于form标题提交的应用技巧(-)
  2. lua学习笔记之模式查找
  3. 3、ACE-实用生活口语---讲打电话Talking on the phone
  4. C语言指针这些使用技巧值得收藏!
  5. 『ORACLE』SPM(下)-baseline实验(11g)
  6. aix查看文件夹大小命令_AIX5.3系统文件大小的限制
  7. 重构wangEditor(web富文本编辑器),欢迎指正!
  8. 安装天文基本包:kapteyn和pyslalib
  9. 应用商店应用计算机,应用市场电脑版
  10. 华为交换机Hybird 与 单臂路由
  11. 在线购物系统—类图设计
  12. 阴阳师android转ios,阴阳师手游IOS自动刷御魂?IOS切换控制教程[多图]
  13. QQ透明头像通用设置教程!不仅简单而且免费!
  14. 计算机综合能力描述,计算机综合应用能力实训报告
  15. 一文深度学习建模预测全流程(Python)
  16. 问题:npm如何设置仓库地址?
  17. 使用requests下载图片,存到图片文件的方法
  18. 视频号匹配时事热点创作内容效果更好
  19. 活动目录(Active Directory) 介绍
  20. 魔塔之拯救白娘子~我的第一个VB6+DX8做的小游戏源码~23开始游戏-存档管理

热门文章

  1. ResultSetMetaData类的介绍
  2. 前端面试中遇到的问题总结
  3. glow 安装使用指南:一款基于终端的 Markdown 阅读器
  4. chmod -R 644 dir04 报错:权限不够
  5. 多线程死锁 危害 解决方案
  6. java中文分词算法
  7. 单片机期末复习 代码篇
  8. java 检测网络连接,使用java检测网络连接情况
  9. 细数霍金关于AI的五大预言,向伟大的科学巨人致敬
  10. 【Unity笔记】使物体(船)漂浮在水面上——浮力