我认为我观察到 .NET JIT 编译器没有内联或优化对没有副作用的空静态方法的调用,考虑到一些定制的在线资源,这有点令人惊讶。
我的环境是 x64、Windows 8.1、.NET Framework 4.5 上的 Visual Studio 2013。
鉴于这个简单的测试程序(https://ideone.com/2BRCpC https://ideone.com/2BRCpC)
class Program
{
static void EmptyBody()
{
}
static void Main()
{
EmptyBody();
}
}
对上述程序进行优化的发布版本会生成以下 MSIL:Main
and EmptyBody
:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 6 (0x6)
.maxstack 8
IL_0000: call void Program::EmptyBody()
IL_0005: ret
} // end of method Program::Main
.method private hidebysig static void EmptyBody() cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method Program::EmptyBody
MSIL 包含来自以下位置的调用并不奇怪Main
to EmptyBody
,因为 C# 编译器预计不会内联或优化此类调用。然而,我认为 JIT 编译器会内联或优化掉该调用。但这似乎并没有发生。
如果我运行上面的程序并进入调试器Main
,生成的程序集是这样的:
00572621 mov ebp,esp
00572623 cmp dword ptr ds:[4320B84h],0
0057262A je 00572631
0057262C call 73E6AF20
00572631 call dword ptr ds:[4321578h]
指令指针立即设置到最后一行 00572631,这是对EmptyBody
。步入EmptyBody
,发现生成的程序集是
00BD2651 mov ebp,esp
00BD2653 cmp dword ptr ds:[4B00B84h],0
00BD265A je 00BD2661
00BD265C call 73E6AF20
00BD2661 nop
00BD2662 pop ebp
00BD2663 ret
指令指针立即设置为nop
00BD2661 处的行,它没有做任何事情,我无法猜测为什么它首先生成。
鉴于上面的两个程序集片段共享相同的 4 指令标头,我假设这只是设置堆栈等的常规方法入口样板。不过,我很想知道这些重复的指令会做什么:
00BD2653 cmp dword ptr ds:[4B00B84h],0
00BD265A je 00BD2661
00BD265C call 73E6AF20
无论如何,主要问题是:为什么 JIT 编译器会生成调用空体静态方法的程序集EmptyBody
?