为什么我的方法调用Assembly.GetCallingAssembly()不是JIT内联?

我正在尝试制作一个简短的C#代码段,它将说明由于MSDN中概述的JIT内联而更改Assembly.GetCallingAssembly()行为。 到目前为止,这是我的代码:

 class Program { static void Main(string[] args) { Console.WriteLine( GetAssembly().FullName ); Console.ReadLine(); } static Assembly GetAssembly() { return System.Reflection.Assembly.GetCallingAssembly(); } } 

我在“Release”中构建并开始使用“Start Without Debugging” – 这个设置使得这个答案的代码产生内联。 我看到的结果是

 ConsoleApplication2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 

很明显GetAssembly()没有内联到Main() ,否则我会看到mscorlib作为调用程序集。

我已经查看了所有内联标准,但我不明白为什么不会内联GetAssembly()

是否有可能知道为什么JIT编译器决定不内联调用?

这是.NET 3.5中Assembly.GetCallingAssembly()的声明:

 [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly GetCallingAssembly() { StackCrawlMark lookForMyCallersCaller = StackCrawlMark.LookForMyCallersCaller; return nGetExecutingAssembly(ref lookForMyCallersCaller); } 

StackCrawlMark枚举很有意思,“查找我的调用者调用者”在调用者内联时无法正常工作。 在CSSCL的SSCLI20源代码中有一条注释,其中声明了枚举:

声明此枚举类型的局部变量并将其通过ref传递给需要执行堆栈爬行的函数将同时阻止内联调用[sic]并将ESP点传递给堆栈爬行到

这与GetCallingAssembly()中发生的事情非常匹配,它是一个局部变量,确实被ref传递。 不知道机制是什么,抖动可以产生名为CORINFO_FLG_BAD_INLINEE的方法属性。 这反过来强制调用MethodDesc :: SetNotInline()。 这是猜测,这是非常模糊的。

加上我的两分钱。 不要依赖JIT 可能会使程序正常运行的事情。

禁止JIT 能够内联方法的一些条件如下(取自此处 )

  • 不会内联大于32字节IL的方法。
  • 虚函数未内联。
  • 具有复杂流量控制的方法将不会被列入。 复杂流量控制是除if / then / else之外的任何流量控制; 在这种情况下,切换或同时。
  • 虽然抛出exception的方法仍然是内联的候选方法,但不会内联包含exception处理块的方法。
  • 如果方法的任何forms参数都是结构,则不会内联该方法。

仅仅因为JIT 可以内联方法,并不意味着它必然会。 将它与构建配置/运行时/操作系统组合之间的行为差​​异结合起来,嗯……你有它。

有关.Net 3.5 SP1 JIT内联行为的更多信息,请点击此处

实际上,您对Program.GetAssembly的调用内联到Program.Main 。 但是你无法看到差异,因为Program.GetAssemblyProgram.Main都是在名为ConsoleApplication2的同一个程序集中定义的。

虽然您可以用另一种方式说明JIT内联:

 using System; using System.Diagnostics; namespace A3 { public class Program { static void Main(string[] args) { StackFrame[] stackFrames = GetStackFrames(); foreach (StackFrame stackFrame in stackFrames) Console.WriteLine(stackFrame.GetMethod().Name); // write method name Console.ReadLine(); } //[MethodImpl(MethodImplOptions.NoInlining)] static StackFrame[] GetStackFrames() { StackTrace stackTrace = new StackTrace(); // get call stack return stackTrace.GetFrames(); // get method calls (frames) } } } 

如果没有JIT内联(例如在调试模式下或者[MethodImpl(MethodImplOptions.NoInlining)]属性应用于GetStackFrames ),它将至少向控制台写入两行:

  1. GetStackFrames
  2. Main

但如果内联发生, stackFrames将只包含一个方法: Main

更新

另外,正如您可以在这里阅读的那样: 调试和托管过程以及他在评论中提到的Rawling:

Assembly.GetCallingAssembly()。FullName返回不同​​的结果,具体取决于是否启用了托管进程。 如果在启用托管进程的情况下调用Assembly.GetCallingAssembly()。FullName,则返回mscorlib。 如果在禁用托管进程的情况下调用Assembly.GetCallingAssembly()。FullName,则返回应用程序名称。