我正在尝试制作一个简短的C#代码段,用于说明由于JIT内联outlined in MSDN而导致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” - 这个设置使this answer的代码产生内联。我看到的结果是
ConsoleApplication2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
很明显GetAssembly()
没有内嵌到Main()
,否则我会看到mscorlib
作为调用程序集。
我已经审核了所有内联标准,但我不明白为什么GetAssembly()
不会被内联。
在某种程度上可以知道为什么JIT编译器决定不内联调用吗?
答案 0 :(得分:8)
这是.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()。这是一个猜测,这是非常模糊的。
答案 1 :(得分:3)
加上我的两分钱。不要依赖JIT 可能做的事情来使程序正常运行。
某些禁止JIT 能够内联方法的条件如下(取自here)
仅仅因为JIT 可以内联方法,并不意味着它必然会。将它与构建配置/运行时/操作系统组合之间的行为差异结合起来,嗯......你有它。
有关.Net 3.5 SP1 JIT内联行为here
的更多信息答案 2 :(得分:1)
实际上,您对Program.GetAssembly
的电话已内联到Program.Main
。但是您无法看到差异,因为Program.GetAssembly
和Program.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
),它将至少向控制台写入两行:
GetStackFrames
Main
但如果内联发生,stackFrames
将只包含一种方法:Main
<强>更新强>
另外,正如你可以在这里阅读:Debugging and the Hosting Process和罗林在评论中提到的那样:
Assembly.GetCallingAssembly()。FullName返回不同的结果 取决于是否启用了托管过程。如果你打电话 Assembly.GetCallingAssembly()。FullName与托管进程 启用后,它返回mscorlib。如果你打电话 Assembly.GetCallingAssembly()。FullName与托管进程 禁用,它返回应用程序名称。