首页 > 其他分享 >Substring 在BCL和CLR里面搞了啥

Substring 在BCL和CLR里面搞了啥

时间:2022-11-07 12:34:15浏览次数:65  
标签:BCL 00007ffd string int coreclr dll Substring FastAllocateString CLR

楔子

还是做点事情,不要那么散漫。

本文以简单的Substring(int startindex,int Length)函数为例,来递进下它在托管和非托管的一些行为。

以下均为个人理解,如有疏漏请指正。



定义和实现

它的定义是在System.Runtime.dll里面

public string Substring(int startIndex, int length)
{
   throw null;
}

它的实现在System.Private.CoreLib.dll里面

  public string Substring(int startIndex, int length)
  {
    //此处省略一万字
     return InternalSubString(startIndex, length);
  }

继续来看下InternalSubString

private string InternalSubString(int startIndex, int length)
{
	string text = string.FastAllocateString(length);
	UIntPtr elementCount = (UIntPtr)text.Length;
	Buffer.Memmove<char>(ref text._firstChar, Unsafe.Add<char>(ref this._firstChar, (IntPtr)((UIntPtr)startIndex)), elementCount);
	return text;
}

FastAllocateString是个FCall函数(也就是微软提供的在托管里面调用非托管的一种方式,它的实际实现是在JIT里面)

	[MethodImpl(MethodImplOptions.InternalCall)]
	internal static extern string FastAllocateString(int length);

Buffer.Memmove是个托管函数,它的作用主要是把FastAllocateString返回的string对象赋值为startIndex和elementCount中间的字符串。过程是利用了Unsafe.Add(它的定义在System.Runtime.CompilerServices,实现实在CLR里面)指针偏移来实现,过程比较简单,不赘述。



FastAllocateString

重点在于这个函数,这个函数进入到了非托管。它进入的方式是通过RyuJit加载这个方法的IL代码。然后对这个IL代码进行解析,重构成汇编代码。

它的非托管原型如下:

#define _DYNAMICALLY_ASSIGNED_FCALLS_BASE() \
    DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateString,                FramedAllocateString) \

FramedAllocateString原型如下:

HCIMPL1(StringObject*, FramedAllocateString, DWORD stringLength)
{
    FCALL_CONTRACT;

    STRINGREF result = NULL;
    HELPER_METHOD_FRAME_BEGIN_RET_0();    // Set up a frame

    result = AllocateString(stringLength);

    HELPER_METHOD_FRAME_END();
    return((StringObject*) OBJECTREFToObject(result));
}
HCIMPLEND

注意了,FastAllocateString实际上调用的不是FramedAllocateString。因为在CLR启动加载的时候,FastAllocateString被替换成了FCall函数形式的调用

ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastMP_InlineGetThread), ECall::FastAllocateString);

DynamicallyAssignFCallImpl原型:

void ECall::DynamicallyAssignFCallImpl(PCODE impl, DWORD index)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    _ASSERTE(index < NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS);
    g_FCDynamicallyAssignedImplementations[index] = impl;
}

可以看到FastAllocateString作为了索引Index,而他的实现是AllocateStringFastMP_InlineGetThread。

再来看下它的堆栈

>	coreclr.dll!ECall::DynamicallyAssignFCallImpl(unsigned __int64 0x00007ffdeed5df50, unsigned long 0x061b1d50)	C++
 	coreclr.dll!InitJITHelpers1()	C++
 	coreclr.dll!EEStartupHelper()	C++
 	coreclr.dll!`EEStartup'::`9'::__Body::Run(void * 0x0000000000000000)	C++
 	coreclr.dll!EEStartup()	C++
 	coreclr.dll!EnsureEEStarted()	C++
 	coreclr.dll!CorHost2::Start()	C++
 	coreclr.dll!coreclr_initialize(const char * 

很明显它是在CLR初始化的时候被替代的



何时被调用

最后一个问题,既然FastAllocateString被替代了,那它何时被调用的呢?

在代码:

private string InternalSubString(int startIndex, int length)
{
	string text = string.FastAllocateString(length);
	UIntPtr elementCount = (UIntPtr)text.Length;
	Buffer.Memmove<char>(ref text._firstChar, Unsafe.Add<char>(ref this._firstChar, (IntPtr)((UIntPtr)startIndex)), elementCount);
	return text;
}

这里面调用了string.FastAllocateString函数,通过上面推断,实际上它已经被被替换了。注意了,但是替换之前,还得按照CLR内存模型进行运作调用。当我们调用InternalSubString的时候,里面调用了FastAllocateString,后者通过PrecodeFixupThunk来进行替换。

这点可以通过汇编验证:

System_Private_CoreLib!System.String.InternalSubString+0xc:
00007ffd`9a86132c 418bc8          mov     ecx,r8d
0:000> t
System_Private_CoreLib!System.String.InternalSubString+0xf:
00007ffd`9a86132f ff15b39f7e00    call    qword ptr [System_Private_CoreLib+0x9cb2e8 (00007ffd`9b04b2e8)] ds:00007ffd`9b04b2e8={coreclr!AllocateStringFastMP_InlineGetThread (00007ffd`9b20b3a0)}
0:000> t
coreclr!AllocateStringFastMP_InlineGetThread:
00007ffd`9b20b3a0 4c8b0d090d3400  mov     r9,qword ptr [coreclr!g_pStringClass (00007ffd`9b54c0b0)] ds:00007ffd`9b54c0b0=00007ffd3b6ed698
call    qword ptr [System_Private_CoreLib+0x9cb2e8 (00007ffd`9b04b2e8)] ds:00007ffd`9b04b2e8={coreclr!AllocateStringFastMP_InlineGetThread (00007ffd`9b20b3a0)}
就是直接调用了AllocateStringFastMP_InlineGetThread,然后跳转到后者的地址


AllocateStringFastMP_InlineGetThread

这个函数的作用实际上是申请内存,比如你 new 一个对象的时候,又或者本例,你需要一个新的字符串对象来存储截取的字符串。
image



作者:江湖评谈
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
image

标签:BCL,00007ffd,string,int,coreclr,dll,Substring,FastAllocateString,CLR
From: https://www.cnblogs.com/tangyanzhi1111/p/16865483.html

相关文章

  • [LeetCode] 1668. Maximum Repeating Substring
    Forastring sequence,astring word is k-repeating if word concatenated k timesisasubstringof sequence.The word's maximum k-repeatingvalue......
  • [数据库基础]-- 字符串截取函数substr、substring以及 case when函数使用
    使用说明:1、使用:substr使用范围:oracle、mysql、sqlserversubstring使用范围:mysql、sqlserver 2、举例:现有表:t_user name、age字段查询需求:如果name字段中的第5个字符有“......
  • CF1729G Cut Substrings 题解
    CF1729GCutSubstrings给出两个字符串\(s,t\),每次可以将字符串\(s\)中任意一个为\(t\)的子串删除,删除位置的字符变为空格(或理解为无实义)。求最少删除几次可以使得......
  • 错误 1 可访问性不一致: 基类“BaseDemo.BClass”比类“BaseDemo.DClass”的可访问性
    1usingSystem;2usingSystem.Collections.Generic;3usingSystem.Linq;4usingSystem.Text;5usingSystem.Drawing;6usingSystem.Windows.Forms;7......
  • 【ABC196F】Substring 2(多项式乘法)
    我竟然能在AT当场做出F题!哦,是ABC啊,没事了。以下的字符串均从\(1\)开始记位。以下设\(S_i\)表示字符串\(S\)的第\(i\)位,\(S(l,r)\)表示字符串\(S\)的第......
  • 「ARC151E」Keep Being Substring - 题解
    题意题目链接:Link。有一个序列\(A\)。\(X,Y\)是给定的\(A\)的两个子串,每次可以在\(X\)的开头或末尾增添或删除一个数字,且需满足任意时刻\(X\)非空且为\(A\)的......
  • 微软正式发布了Microsoft.Bcl.Async
    微软发布了Microsoft.Bcl.Async的最终版本,参看博客​​Microsoft.Bcl.AsyncisNowStable​​​。该包允许开发者在.NET4、Silverlight4和WindowsPhone7.5使用C#5和V......
  • subStr和subString以及slice的使用和区别
    substr相关使用注意事项第一个参数必须为数值,可正可负可为0为负数则从倒数开始第二个参数是指定长度,默认为到字符串最后如果第一个参数超过字符串的长度,那么会返回......
  • CLR GC FinalizeQueue 深入剖析
    引子最近开始看这个起因其实是在看项目代码时发现实现Disposable模式时,项目代码中感觉有些地方实现的没有这个必要,和同事讨论之后,无果,索性就又去研究了下CLRGC中关......
  • Object-C isSubclassOfClass
    一.isSubclassOfClass简介/*判断是否为对象是否为ClassName或其子类的实例*/-(BOOL)isSubclassOfClass:(Class)aClass;isSubclassOfClass 和 isKindOfClass的作......