더글러스의 답변은 JIT 최적화 데드 코드 (모두 x86 및 x64 컴파일러가이 작업을 수행함)에 대한 답변입니다. 그러나 JIT 컴파일러가 불량 코드를 최적화하는 경우 x
이 로컬 창에 나타나지 않기 때문에 즉시 명백하게 알 수 있습니다. 또한 시계 및 직접 실행 창은 "현재 컨텍스트에 'x'이름이 없습니다."라는 오류 메시지를 표시합니다. 그것은 당신이 일어난 것으로 묘사 한 것이 아닙니다. Win7x64 및 VS2012 :
은 당신이보고있는 것은 실제로 2010 년
먼저 Visual Studio에서 버그, 내 메인 컴퓨터에서이 문제를 재현 해 보았습니다. .NET 4.0 타겟의 경우, 닫는 중괄호가 깨질 때 x
은 3.0D와 같습니다. 나는 .NET 3.5 타겟도 시도하기로 결정했고, 그걸로 x
도 3.0D가 아니라 null로 설정되었다.
.NET 4.0에 .NET 4.5를 설치했기 때문에이 문제를 완벽하게 재현 할 수 없으므로 가상 컴퓨터를 설치하고 VS2010을 설치했습니다.
여기에서 문제를 재현 할 수있었습니다. 시계 창과 지역 창 둘 다에서 Main
메서드의 닫는 중괄호에 중단 점을 사용하여 x
이 null
임을 확인했습니다. 이것은 흥미로운 곳입니다. 대신 v2.0 런타임을 대상으로하고 null도 발견했습니다.분명히 다른 컴퓨터에서 동일한 버전의 .NET 2.0 런타임을 가지고 있기 때문에 의 값이 3.0D
인 것으로 나타 났으므로 확실하지 않습니다.
그럼, 어떻게 될까요? windbg에서 파기 한 후 다음과 같은 문제를 발견했습니다.
VS2010은 실제로이 할당되기 전에 x 값을 표시합니다.
명령어 포인터가 x = y + z
라인을 넘었 기 때문에 모양이 다르다는 것을 알고 있습니다. 3.0D
에 동등한 x
을 보여줍니다 최종 중괄호, 지역 주민에 중단 점으로
double? y = 1D;
double? z = 2D;
double? x;
x = y + z;
Console.WriteLine(); // Don't reference x here, still leave it as dead code
을하고 창을 시계 : 당신은 방법에 몇 줄의 코드를 추가하여이 직접 테스트 할 수 있습니다. 그러나 코드를 단계별로 실행하면 Console.WriteLine()
을 통해 수행 한 이후에 VS2010에 까지 할당 된 것으로 표시되는 x
이 표시되지 않습니다.
이 버그가 Microsoft Connect에보고 된 적이 있는지는 잘 모르겠지만이 코드를 예로 들어 설명해보십시오. VS2012에서는 분명히 수정되었으므로이 문제를 수정하는 업데이트가 있는지 확실하지 않습니다.
다음은 실제로 JIT에서 어떤 일이 일어나고 그것이 잘못된 이유 원래의 코드와 VS2010는
, 우리는 VS가 무엇을하고 있는지보고 할 수있는 작업. 또한 x
변수가 최적화되지 않음을 알 수 있습니다 (최적화를 사용하여 컴파일되도록 어셈블리를 표시하지 않은 경우).
먼저,이 IL의 지역 변수 정의를 살펴 보자 :이 디버그 모드에서 정상 출력이
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<float64> y,
[1] valuetype [mscorlib]System.Nullable`1<float64> z,
[2] valuetype [mscorlib]System.Nullable`1<float64> x,
[3] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0000,
[4] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0001,
[5] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0002)
. Visual Studio는 할당 중에 사용하는 중복 로컬 변수를 정의한 다음 추가 IL 명령을 추가하여 CS * 변수에서 해당 변수를 사용자 정의 로컬 변수로 복사합니다. 당신이 VS2010에서 응용 프로그램을 디버깅 및 방법의 말, 우리가 할 수에서 브레이크 포인트를두면
:
// For the line x = y + z
L_0045: ldloca.s CS$0$0000 // earlier, y was stloc.3 (CS$0$0000)
L_0047: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_004c: conv.r8 // Convert to a double
L_004d: ldloca.s CS$0$0001 // earlier, z was stloc.s CS$0$0001
L_004f: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_0054: conv.r8 // Convert to a double
L_0055: add // Add them together
L_0056: newobj instance void [mscorlib]System.Nullable`1<float64>::.ctor(!0) // Create a new nulable
L_005b: nop // NOPs are placed in for debugging purposes
L_005c: stloc.2 // Save the newly created nullable into `x`
L_005d: ret
하는의 WinDbg는 몇 가지 더 깊은 디버깅을하자 : 여기에 이런 일을 보여줍니다 해당 IL 코드는 비 침습적 인 모드에서 WinDbg를 쉽게 부착하십시오.
다음은 호출 스택의 Main
메서드에 대한 프레임입니다. 우리는 IP (instruction pointer)에 관심이있다.
0:009> !clrstack
OS Thread Id: 0x135c (9)
Child SP IP Call Site
000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main(System.String[])
[And so on...]
우리가 Main
방법에 대한 기본 시스템 코드를 볼 경우, 우리는 지시 VS 실행이 중단되는 시간에 실행 된 것을 볼 수있다 : 우리가에서 가져온 현재 IP를 사용
000007ff`00173388 e813fe25f2 call mscorlib_ni+0xd431a0
(000007fe`f23d31a0) (System.Nullable`1[[System.Double, mscorlib]]..ctor(Double), mdToken: 0000000006001ef2)
****000007ff`0017338d cc int 3****
000007ff`0017338e 8d8c2490000000 lea ecx,[rsp+90h]
000007ff`00173395 488b01 mov rax,qword ptr [rcx]
000007ff`00173398 4889842480000000 mov qword ptr [rsp+80h],rax
000007ff`001733a0 488b4108 mov rax,qword ptr [rcx+8]
000007ff`001733a4 4889842488000000 mov qword ptr [rsp+88h],rax
000007ff`001733ac 488d8c2480000000 lea rcx,[rsp+80h]
000007ff`001733b4 488b01 mov rax,qword ptr [rcx]
000007ff`001733b7 4889442440 mov qword ptr [rsp+40h],rax
000007ff`001733bc 488b4108 mov rax,qword ptr [rcx+8]
000007ff`001733c0 4889442448 mov qword ptr [rsp+48h],rax
000007ff`001733c5 eb00 jmp 000007ff`001733c7
000007ff`001733c7 0f28b424c0000000 movaps xmm6,xmmword ptr [rsp+0C0h]
000007ff`001733cf 4881c4d8000000 add rsp,0D8h
000007ff`001733d6 c3 ret
을 !clrstack
을 Main
에 넣었을 때, 의 직후에 실행이 중단되었고, System.Nullable<double>
의 생성자가 호출 된 것을 볼 수 있습니다.(int 3
은 디버거가 실행을 중지하는 데 사용하는 인터럽트입니다.) 나는 그 줄을 *로 둘러 쌌으며 IL의 L_0056
줄까지 일치시킬 수도 있습니다.
다음 x64 어셈블리는 실제로이를 로컬 변수 x
에 할당합니다. 명령어 포인터가 아직 해당 코드를 실행하지 않았으므로 VS2010은 x
변수가 네이티브 코드에 의해 할당되기 전에 조기에 중단됩니다.
편집 : 위에서 볼 수 있듯이 x64에서는 int 3
명령어가 할당 코드 앞에 배치됩니다. x86에서는 해당 명령어가 할당 코드 뒤에 배치됩니다. 이것이 VS가 x64에서만 초기에 깨지는 이유를 설명합니다. 이것이 Visual Studio 또는 JIT 컴파일러의 잘못인지는 말할 수 없습니다. 어떤 응용 프로그램이 중단 점 후크를 삽입하는지 확신 할 수 없습니다.
ideone은 모노를 실행하며 예상대로 작동합니다. http://ideone.com/DbuxwD –
이것은 내 이익과 관련이 있습니다. 이제 우리는 Skeet 씨를 기다려야합니다. – MikeTheLiar
디버거가 될 수 있습니까? Console.WriteLine()을 맨 끝에두고 출력 내용을 확인하십시오. – siride