在.Net 7 GC增加GetConfigurationVariables

前言

在.Net Runtime源码的issues看到GC一个关于新增的API,用于获取GC的配置信息. .Net 7新功能的提交已经暂停了,在八月份会发布RC1,同时新功能代码的提交到.Net 8中,后面差不多四个月的时间用来处理Bug和补充新增API的文档.

  1. Added a new method to the GC API to Get the GC Configurations (#70514)

GetConfigurationVariables如何使用

在.Net 7 GC增加GetConfigurationVariables

GetConfigurationVariables如何使用

GetConfigurationVariables使用是比较简单的.这里就不代码展示了,这里看目前获取到GC的配置信息:

在.Net 7 GC增加GetConfigurationVariables

打印GetConfigurationVariables获取到的GC配置信息

GetConfigurationVariables内部源码

//GC配置信息类型有3种,int类型/字符串(utf-8编码)/布尔类型  在RuntimeImports.cs文件
internal enum GCConfigurationType
{
    Int64,
    StringUtf8,
    Boolean
}

internal struct GCConfigurationContext
{
    internal Dictionary Configurations;  //定义一个字典变量,用于存放GC配置信息的key和value
}

[UnmanagedCallersOnly]
private static unsafe void Callback(void* configurationContext, void* name, void* publicKey, RuntimeImports.GCConfigurationType type, long data)
{
    // If the public key is null, it means that the corresponding configuration isn't publicly available
    // and therefore, we shouldn't add it to the configuration dictionary to return to the user.
    if (publicKey == null)
    {
        return;
    }

    Debug.Assert(name != null);
    Debug.Assert(configurationContext != null);

    ref GCConfigurationContext context = ref Unsafe.As(ref *(byte*)configurationContext);
    Debug.Assert(context.Configurations != null);
    Dictionary configurationDictionary = context.Configurations!;

    string nameAsString = Marshal.PtrToStringUTF8((IntPtr)name)!;
    switch (type)
    {
        case RuntimeImports.GCConfigurationType.Int64:  //处理配置value为int类型
            configurationDictionary[nameAsString] = data;
            break;

        case RuntimeImports.GCConfigurationType.StringUtf8: //处理配置value是字符串
            {
                string? dataAsString = Marshal.PtrToStringUTF8((IntPtr)data);
                configurationDictionary[nameAsString] = dataAsString ?? string.Empty;
                break;
            }

        case RuntimeImports.GCConfigurationType.Boolean:   //处理配置value为布尔类型
            configurationDictionary![nameAsString] = data != 0;
            break;
    }
}

/// 
/// Gets the Configurations used by the Garbage Collector. The value of these configurations used don't neccessarily have to be the same as the ones that are passed by the user.
/// For example for the "GCHeapCount" configuration, if the user supplies a value higher than the number of CPUs, the configuration that will be used is that of the number of CPUs.
///  A Read Only Dictionary with configuration names and values of the configuration as the keys and values of the dictionary, respectively.
/// 
public static unsafe IReadOnlyDictionary GetConfigurationVariables()
{
    GCConfigurationContext context = new GCConfigurationContext
    {
        Configurations = new Dictionary()
    };

    //RuntimeImports.cs 声明和定义了与CLR交互的类型和函数原型
    //RhEnumerateConfigurationValues在CLR内部实现的,将GCConfigurationContext转为指针,传递到CLR中,并将Callback作为回调函数传入
    //在CLR内部调用EnumerateConfigurationValues函数,每获取一次GC配置项,插入到context下的Configurations字典中
    RuntimeImports.RhEnumerateConfigurationValues(Unsafe.AsPointer(ref context), &Callback);
    return context.Configurations!;
}

CLR内部EnumerateConfigurationValues函数是如何实现的呢?

//设置GC配置项的值
GCConfig::SetGCLargePages(gc_heap::use_large_pages_p);
GCConfig::SetGCHeapHardLimit(static_cast(gc_heap::heap_hard_limit));
GCConfig::SetGCHeapHardLimitSOH(static_cast(gc_heap::heap_hard_limit_oh[soh]));
GCConfig::SetGCHeapHardLimitLOH(static_cast(gc_heap::heap_hard_limit_oh[loh]));
GCConfig::SetGCHeapHardLimitPOH(static_cast(gc_heap::heap_hard_limit_oh[poh]));

//获取和设置 配置项的值  宏***
#define BOOL_CONFIG(name, unused_private_key, unused_public_key, default, unused_doc) \
  bool GCConfig::Get##name() { return s_##name; }                                     \
  void GCConfig::Set##name(bool value) { s_Updated##name = value; }                   \
  bool GCConfig::s_##name = default;                                                  \
  bool GCConfig::s_Updated##name = default;

#define INT_CONFIG(name, unused_private_key, unused_public_key, default, unused_doc)  \
  int64_t GCConfig::Get##name() { return s_##name; }                                  \
  void GCConfig::Set##name(int64_t value) { s_Updated##name = value; }                \ 
  int64_t GCConfig::s_##name = default;                                               \
  int64_t GCConfig::s_Updated##name = default;

// String configs are not cached because 1) they are rare and
// not on hot paths and 2) they involve transfers of ownership
// of EE-allocated strings, which is potentially complicated.
#define STRING_CONFIG(name, private_key, public_key, unused_doc)  \
  GCConfigStringHolder GCConfig::Get##name()                                       \
  {                                                                                \
      const char* resultStr = nullptr;                                             \
      GCToEEInterface::GetStringConfigValue(private_key, public_key, &resultStr);  \
      return GCConfigStringHolder(resultStr);                                      \
  }

//获取配置的值
void GCConfig::EnumerateConfigurationValues(void* context, ConfigurationValueFunc configurationValueFunc)
{
#define INT_CONFIG(name, unused_private_key, public_key, default, unused_doc) \
    configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::Int64, static_cast(s_Updated##name));
    
#define STRING_CONFIG(name, private_key, public_key, unused_doc)                     \
    {                                                                                \
        const char* resultStr = nullptr;                                             \
        GCToEEInterface::GetStringConfigValue(private_key, public_key, &resultStr);  \
        GCConfigStringHolder holder(resultStr);                                      \
        configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::StringUtf8, reinterpret_cast(resultStr)); \
    }

#define BOOL_CONFIG(name, unused_private_key, public_key, default, unused_doc) \
    configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::Boolean, static_cast(s_Updated##name));

GC_CONFIGURATION_KEYS

#undef BOOL_CONFIG
#undef INT_CONFIG
#undef STRING_CONFIG
}

个人能力有限,如果您发现有什么不对,请私信我

如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章