潜在的.NET x86 JIT问题?

下面的代码在内置发行版模式(或优化debugging时)时运行方式不同,在没有附加Visual Studiodebugging器的情况下运行。

它也似乎只复制如果使用x86 JITter。 我已经在一台x86机器上testing了这一点,并在x64机器上的WOW64上运行(通过将平台目标设置为x86)。

我只用.NET 4.0试过。

在Release中debugging器外部运行时,看到:

Value is 4 

在debugging器内部运行时, WriteLine调用的e.Value.Length部分抛出NullReferenceException ,这是我期望发生的事情。

代码:

 namespace Test { class UsingReleasable<T> { public UsingReleasable(T obj) { m_obj = obj; } public T Release() { T tmp = m_obj; m_obj = default(T); return tmp; } public T Value { get { return m_obj; } } T m_obj; } class Program { static void Main(string[] args) { var e = new UsingReleasable<string>("test"); e.Release(); System.Console.WriteLine("Value is {0}", e.Value.Length); } } } 

我盯着JIT生成的代码,使我觉得这是一个错误,但我想在这里转发到MS Connect之前,仔细检查。

我可以重现你的行为:

 R:\>csc /platform:x86 releasable.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. R:\>releasable Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Test.Program.Main(String[] args) R:\>csc /o+ /platform:x86 releasable.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. R:\>releasable Value is 4 R:\>csc /platform:anycpu releasable.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. R:\>releasable Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Test.Program.Main(String[] args) R:\>csc /o+ /platform:anycpu releasable.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. R:\>releasable Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Test.Program.Main(String[] args) 

/checked编译器选项没有区别。 也不会产生debugging数据/debug+ 。 当调用Console.ReadLine()时,问题仍然存在(为了给玩家一个附加debugging器的机会,并查看优化的代码。


我对Main进行了一些修改,以允许优化代码的debugging:

  static void Main(string[] args) { var e = new UsingReleasable<string>("test"); System.Console.WriteLine("attach now"); System.Console.ReadLine(); e.Release(); System.Console.WriteLine("Value is {0}", e.Value.Length); } 

而实际的拆解:

 --- r:\releasable.cs ----------------------------------------------------------- var e = new UsingReleasable<string>("test"); 00000000 push ebp 00000001 mov ebp,esp 00000003 push edi 00000004 push esi 00000005 mov ecx,1839B0h 0000000a call FFF81FB0 0000000f mov esi,eax 00000011 mov eax,dword ptr ds:[03772030h] 00000017 lea edx,[esi+4] 0000001a call 60452F70 System.Console.WriteLine("attach now"); 0000001f call 5E927060 00000024 mov ecx,eax 00000026 mov edx,dword ptr ds:[03772034h] 0000002c mov eax,dword ptr [ecx] 0000002e mov eax,dword ptr [eax+3Ch] 00000031 call dword ptr [eax+10h] 00000034 call 5EEF9A40 00000039 mov ecx,eax 0000003b mov eax,dword ptr [ecx] 0000003d mov eax,dword ptr [eax+2Ch] 00000040 call dword ptr [eax+1Ch] e.Release(); 00000043 mov edi,dword ptr [esi+4] ; edi = e.Value 00000046 lea esi,[esi+4] ; esi = &e.Value 00000049 xor edx,edx ; edx = null 0000004b mov dword ptr [esi],edx ; *esi = edx (e.Value = null) 0000004d mov ecx,5EBE28F8h 00000052 call FFF81FB0 00000057 mov edx,eax 00000059 mov eax,dword ptr [edi+4] ; this sets EAX to 4 0000005c mov dword ptr [edx+4],eax 0000005f mov esi,edx 00000061 call 5E927060 00000066 push esi 00000067 mov ecx,eax 00000069 mov edx,dword ptr ds:[03772038h] 0000006f mov eax,dword ptr [ecx] 00000071 mov eax,dword ptr [eax+3Ch] 00000074 call dword ptr [eax+18h] ; this results in the output "Value is 4\n" 00000077 pop esi } 00000078 pop edi 00000079 pop ebp 0000007a ret 

当程序在debugging器下启动时,会生成此代码(并生成NullReferenceException

 --- r:\releasable.cs ----------------------------------------------------------- var e = new UsingReleasable<string>("test"); 00000000 push ebp 00000001 mov ebp,esp 00000003 sub esp,24h 00000006 mov dword ptr [ebp-4],ecx 00000009 cmp dword ptr ds:[001E313Ch],0 00000010 je 00000017 00000012 call 606B6807 00000017 xor edx,edx 00000019 mov dword ptr [ebp-0Ch],edx 0000001c mov ecx,1E39B0h 00000021 call FFF91FB0 00000026 mov dword ptr [ebp-10h],eax 00000029 mov edx,dword ptr ds:[032E2030h] 0000002f mov ecx,dword ptr [ebp-10h] 00000032 call dword ptr ds:[001E3990h] 00000038 mov eax,dword ptr [ebp-10h] 0000003b mov dword ptr [ebp-0Ch],eax System.Console.WriteLine("attach now"); 0000003e mov ecx,dword ptr ds:[032E2034h] 00000044 call 5E8D703C System.Console.ReadLine(); 00000049 call 5EEAA728 0000004e nop e.Release(); 0000004f mov ecx,dword ptr [ebp-0Ch] 00000052 cmp dword ptr [ecx],ecx 00000054 call dword ptr ds:[001E3994h] 0000005a nop System.Console.WriteLine("Value is {0}", e.Value.Length); 0000005b mov eax,dword ptr ds:[032E2038h] 00000061 mov dword ptr [ebp-14h],eax 00000064 mov ecx,dword ptr [ebp-0Ch] 00000067 cmp dword ptr [ecx],ecx 00000069 call dword ptr ds:[001E3998h] 0000006f mov dword ptr [ebp-18h],eax 00000072 mov ecx,dword ptr [ebp-18h] 00000075 cmp dword ptr [ecx],ecx ; access violation here 00000077 call 608CBA5B 0000007c mov dword ptr [ebp-8],eax 0000007f mov ecx,5EBE28F8h 00000084 call FFF91FB0 00000089 mov dword ptr [ebp-1Ch],eax 0000008c mov eax,dword ptr [ebp-14h] 0000008f mov dword ptr [ebp-20h],eax 00000092 mov eax,dword ptr [ebp-1Ch] 00000095 mov edx,dword ptr [ebp-8] 00000098 mov dword ptr [eax+4],edx 0000009b mov eax,dword ptr [ebp-1Ch] 0000009e mov dword ptr [ebp-24h],eax 000000a1 mov ecx,dword ptr [ebp-20h] 000000a4 mov edx,dword ptr [ebp-24h] 000000a7 call 5E8CD460 } 000000ac nop 000000ad mov esp,ebp 000000af pop ebp 000000b0 ret 

我想我已经评论了不正确版本的所有相关代码行。 很明显,寄存器edi被用来保存一个指向e.Value的指针,该指针最有可能是指向内容的指针(偏移量为0)和 v-表的长度(偏移量为4) (偏移量为0),长度为偏移量4),紧接着是内容。 不幸的是e.Value (string"test" )在e.Value被清除之前被复制到edi ,所以使用错误的string指针来获取长度。 哎哟!


错误提交连接(请upvote!): x86 JIT不正确地重新sorting通用types字段的负载与赋值为默认(T)