성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - 생성한 참조 개체가 언제 GC의 정리 대상이 될까요?</h1> <p> 참조 타입의 인스턴스는 그것을 참조하는 루트 개체가 없을 때 정리될 수 있습니다. 그리고 해당 루트 개체에는 CPU의 레지스터나 메모리의 스택 영역도 포함이 되는데요, 따라서 일반적으로는 레지스터나 스택이 개체의 GC 힙 주소를 담고 있다면 그것은 GC가 되어서는 안 됩니다.<br /> <br /> 이것을 코드로 표현해볼까요? 가령 다음과 같은 상황에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void Main(string[] args) { object hr = new object(); hr.ToString(); // hr 인스턴스 사용 Console.WriteLine("TEST"); } </pre> <br /> hr 개체는 Main 메서드가 종료될 때까지 GC가 되지 않을 거라고 예상할 수 있습니다. 왜냐하면, hr 변수는 스택 또는 (최적화에 의해) 레지스터에 보관될 것이고, Main 메서드가 끝날 때까지는 유지될 것이기 때문에 GC가 되진 않을 것이기 때문입니다.<br /> <br /> 그래서 간혹 개발자들은 hr 인스턴스를 사용하지 않게 되었을 때 일부러 GC가 되게끔 null 값을 지정하곤 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void Main(string[] args) { object hr = new object(); hr.ToString(); // 마지막 hr 인스턴스 사용 지점. hr = null; // GC 대상이 되도록! Console.WriteLine("TEST"); } </pre> <br /> 일반적인 상황이라면, 저렇게 동작이 되는 것이 맞습니다. 하지만, "JIT 컴파일러"가 수행하는 최적화에 의해 저런 가정은 올바르지 않습니다. 왜냐하면, 똑똑한 JIT 컴파일러는 hr 개체가 할당이 된 후 ToString 메서드가 호출된 다음부터 더 이상 사용을 안 하고 있는 것을 알 수 있기 때문입니다.<br /> <br /> 따라서, 저 IL 코드를 "Jitting"하는 과정에서 hr 변수가 더 이상 사용되지 않는 시점, 즉 ToString을 호출한 이후부터는 hr 개체를 GC 대상으로 판정하는 최적화를 (경우에 따라) 수행합니다.<br /> <br /> 이에 대한 재현을 다음과 같이 할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>// Release 빌드</span> using System; internal class Program { static void Main(string[] args) { object hr = new object(); WeakReference wr = new WeakReference(hr); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); <span style='color: blue; font-weight: bold'>Console.WriteLine($"Target == {wr.Target}");</span> for (; ;) { Console.ReadLine(); } } } </pre> <br /> hr 인스턴스가 할당되었지만, JIT 컴파일러는 해당 개체가 new WeakReference 호출 이후 사용되지 않는다는 것을 알 수 있어, 그 후부터 GC 대상이 되게끔 최적화를 수행합니다. 그래서 GC.Collect 호출에서 hr 개체는 해제되고 화면에는 "Target == " 문자열만 출력이 됩니다.<br /> <br /> 즉, 굳이 개발자가 "hr = null;" 코드를 넣지 않아도 되는 것입니다.<br /> <br /> 여기서 한 가지 재미있는 점은, 마지막의 "for (; ;)" 코드를 주석 처리하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // ...[생략]... Console.WriteLine($"Target == {wr.Target}"); <span style='color: blue; font-weight: bold'>// for (; ;)</span> { Console.ReadLine(); } </pre> <br /> 그다음부터는 다시 "Target == System.Object" 출력을 얻게 됩니다. 아마도, JIT 컴파일러의 최적화 코드는, 변수 생성 후 scope을 벗어나기까지 실행되는 코드가 유의미하지 않다면 (어차피 빠르게 지나갈 수 있으므로) 굳이 변수의 수명을 계산하는 작업은 하지 않는 듯합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 저게 전부가 아닙니다. 실제로 위에서 제시한 코드를 disassembly 창으로 보면 다음과 같이 나옵니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; internal class Program { static void Main(string[] args) { object hr = new object(); 00007FF9101138A0 push rbp 00007FF9101138A1 push r15 00007FF9101138A3 push r14 00007FF9101138A5 push r13 00007FF9101138A7 push r12 00007FF9101138A9 push rdi 00007FF9101138AA push rsi 00007FF9101138AB push rbx 00007FF9101138AC sub rsp,98h 00007FF9101138B3 lea rbp,[rsp+0D0h] 00007FF9101138BB vxorps xmm4,xmm4,xmm4 00007FF9101138BF vmovdqa xmmword ptr [rbp-60h],xmm4 00007FF9101138C4 vmovdqa xmmword ptr [rbp-50h],xmm4 00007FF9101138C9 xor edx,edx 00007FF9101138CB mov qword ptr [rbp-40h],rdx 00007FF9101138CF lea rcx,[rbp-0A8h] 00007FF9101138D6 mov rdx,r10 00007FF9101138D9 call 00007FF96FBD5480 00007FF9101138DE mov rsi,rax 00007FF9101138E1 mov rcx,rsp 00007FF9101138E4 mov qword ptr [rbp-88h],rcx 00007FF9101138EB mov rcx,rbp 00007FF9101138EE mov qword ptr [rbp-78h],rcx 00007FF9101138F2 mov rcx,7FF9100F5678h 00007FF9101138FC call CORINFO_HELP_NEWSFAST (07FF96FC8AFD0h) 00007FF910113901 <span style='color: blue; font-weight: bold'>mov rdi,rax</span> WeakReference wr = new WeakReference(hr); 00007FF910113904 mov rcx,7FF9101E3820h 00007FF91011390E call 00007FF96FB5AA10 00007FF910113913 mov rbx,rax 00007FF910113916 mov rcx,rbx 00007FF910113919 <span style='color: blue; font-weight: bold'>mov rdx,rdi</span> 00007FF91011391C xor r8d,r8d 00007FF91011391F call 00007FF96FC7A180 00007FF910113924 mov qword ptr [rbp-68h],rbx GC.Collect(); 00007FF910113928 mov ecx,0FFFFFFFFh 00007FF91011392D mov edx,2 00007FF910113932 mov rax,7FF9101E3E90h 00007FF91011393C mov qword ptr [rbp-98h],rax 00007FF910113943 lea rax,[Program.Main(System.String[])+0C3h (07FF910113963h)] 00007FF91011394A mov qword ptr [rbp-80h],rax 00007FF91011394E lea rax,[rbp-0A8h] 00007FF910113955 mov qword ptr [rsi+10h],rax 00007FF910113959 mov byte ptr [rsi+0Ch],0 00007FF91011395D call qword ptr [7FF9101E44A8h] 00007FF910113963 mov byte ptr [rsi+0Ch],1 00007FF910113967 cmp dword ptr [7FF96FFD4A34h],0 00007FF91011396E je Program.Main(System.String[])+0D6h (07FF910113976h) 00007FF910113970 call qword ptr [Pointer to: CORINFO_HELP_STOP_FOR_GC (07FF96FFC6398h)] 00007FF910113976 mov rax,qword ptr [rbp-0A0h] 00007FF91011397D mov qword ptr [rsi+10h],rax GC.WaitForPendingFinalizers(); 00007FF910113981 mov rax,7FF9101E4100h 00007FF91011398B mov qword ptr [rbp-98h],rax 00007FF910113992 lea rax,[Program.Main(System.String[])+0112h (07FF9101139B2h)] 00007FF910113999 mov qword ptr [rbp-80h],rax 00007FF91011399D lea rax,[rbp-0A8h] 00007FF9101139A4 mov qword ptr [rsi+10h],rax 00007FF9101139A8 mov byte ptr [rsi+0Ch],0 00007FF9101139AC call qword ptr [7FF9101E44C0h] 00007FF9101139B2 mov byte ptr [rsi+0Ch],1 00007FF9101139B6 cmp dword ptr [7FF96FFD4A34h],0 00007FF9101139BD je Program.Main(System.String[])+0125h (07FF9101139C5h) 00007FF9101139BF call qword ptr [Pointer to: CORINFO_HELP_STOP_FOR_GC (07FF96FFC6398h)] 00007FF9101139C5 mov rcx,qword ptr [rbp-0A0h] 00007FF9101139CC mov qword ptr [rsi+10h],rcx GC.Collect(); 00007FF9101139D0 mov ecx,0FFFFFFFFh 00007FF9101139D5 mov edx,2 00007FF9101139DA mov rax,7FF9101E3E90h 00007FF9101139E4 mov qword ptr [rbp-98h],rax 00007FF9101139EB lea rax,[Program.Main(System.String[])+016Bh (07FF910113A0Bh)] 00007FF9101139F2 mov qword ptr [rbp-80h],rax 00007FF9101139F6 lea rax,[rbp-0A8h] 00007FF9101139FD mov qword ptr [rsi+10h],rax 00007FF910113A01 mov byte ptr [rsi+0Ch],0 00007FF910113A05 call qword ptr [7FF9101E44A8h] 00007FF910113A0B mov byte ptr [rsi+0Ch],1 00007FF910113A0F cmp dword ptr [7FF96FFD4A34h],0 00007FF910113A16 je Program.Main(System.String[])+017Eh (07FF910113A1Eh) 00007FF910113A18 call qword ptr [Pointer to: CORINFO_HELP_STOP_FOR_GC (07FF96FFC6398h)] 00007FF910113A1E mov rcx,qword ptr [rbp-0A0h] 00007FF910113A25 mov qword ptr [rsi+10h],rcx Console.WriteLine($"Target == {wr.Target}"); 00007FF910113A29 mov rcx,7FF9100F4928h 00007FF910113A33 mov edx,5 00007FF910113A38 call 00007FF96FC426C0 00007FF910113A3D mov rcx,22518002F08h 00007FF910113A47 mov rcx,qword ptr [rcx] 00007FF910113A4A mov edx,100h 00007FF910113A4F cmp dword ptr [rcx],ecx 00007FF910113A51 call qword ptr [Pointer to: CLRStub[MethodDescPrestub]@7ff9101191f0 (07FF9101E6AF0h)] 00007FF910113A57 lea rcx,[rbp-60h] 00007FF910113A5B mov qword ptr [rbp-58h],rax 00007FF910113A5F test rax,rax 00007FF910113A62 jne Program.Main(System.String[])+01CBh (07FF910113A6Bh) 00007FF910113A64 xor edx,edx 00007FF910113A66 xor r8d,r8d 00007FF910113A69 jmp Program.Main(System.String[])+01D3h (07FF910113A73h) 00007FF910113A6B lea rdx,[rax+10h] 00007FF910113A6F mov r8d,dword ptr [rax+8] 00007FF910113A73 add rcx,18h 00007FF910113A77 mov qword ptr [rcx],rdx 00007FF910113A7A mov dword ptr [rcx+8],r8d 00007FF910113A7E xor ecx,ecx 00007FF910113A80 mov dword ptr [rbp-50h],ecx 00007FF910113A83 mov byte ptr [rbp-4Ch],0 00007FF910113A87 mov ecx,dword ptr [rbp-50h] 00007FF910113A8A cmp ecx,dword ptr [rbp-40h] 00007FF910113A8D ja Program.Main(System.String[])+0292h (07FF910113B32h) 00007FF910113A93 mov rdx,qword ptr [rbp-48h] 00007FF910113A97 mov r8d,dword ptr [rbp-40h] 00007FF910113A9B sub r8d,ecx 00007FF910113A9E mov ecx,ecx 00007FF910113AA0 lea rcx,[rdx+rcx*2] 00007FF910113AA4 cmp r8d,0Ah 00007FF910113AA8 jb Program.Main(System.String[])+0231h (07FF910113AD1h) 00007FF910113AAA mov rdx,225180030D8h 00007FF910113AB4 mov rdx,qword ptr [rdx] 00007FF910113AB7 add rdx,0Ch 00007FF910113ABB mov r8d,14h 00007FF910113AC1 call Method stub for: System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) (07FF91010E808h) 00007FF910113AC6 mov edx,dword ptr [rbp-50h] 00007FF910113AC9 add edx,0Ah 00007FF910113ACC mov dword ptr [rbp-50h],edx 00007FF910113ACF jmp Program.Main(System.String[])+0247h (07FF910113AE7h) 00007FF910113AD1 mov rdx,225180030D8h 00007FF910113ADB mov rdx,qword ptr [rdx] 00007FF910113ADE lea rcx,[rbp-60h] 00007FF910113AE2 call CLRStub[MethodDescPrestub]@7ff9101125f8 (07FF9101125F8h) 00007FF910113AE7 mov rcx,qword ptr [rbp-68h] 00007FF910113AEB call 00007FF96FC80CD0 00007FF910113AF0 mov r8,rax 00007FF910113AF3 lea rcx,[rbp-60h] 00007FF910113AF7 mov rdx,7FF9101E4618h 00007FF910113B01 call CLRStub[MethodDescPrestub]@7ff910112890 (07FF910112890h) 00007FF910113B06 lea rcx,[rbp-60h] 00007FF910113B0A call CLRStub[MethodDescPrestub]@7ff910112560 (07FF910112560h) 00007FF910113B0F mov rcx,rax 00007FF910113B12 call CLRStub[MethodDescPrestub]@7ff910112cc8 (07FF910112CC8h) for (; ;) { Console.ReadLine(); 00007FF910113B17 call CLRStub[MethodDescPrestub]@7ff910112c58 (07FF910112C58h) 00007FF910113B1C jmp Program.Main(System.String[])+0277h (07FF910113B17h) 00007FF910113B1E add rsp,98h 00007FF910113B25 pop rbx 00007FF910113B26 pop rsi 00007FF910113B27 <span style='color: blue; font-weight: bold'>pop rdi</span> 00007FF910113B28 pop r12 00007FF910113B2A pop r13 00007FF910113B2C pop r14 00007FF910113B2E pop r15 00007FF910113B30 pop rbp 00007FF910113B31 ret </pre> <br /> 여기서, object hr 변수를 담고 있는 레지스터만을 정리하면 딱 3군데가 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ...[생략]... 00007FF9101138FC call CORINFO_HELP_NEWSFAST (07FF96FC8AFD0h) 00007FF910113901 mov rdi,rax ...[생략]... mov rdx,rdi ...[생략]... 00007FF910113B27 pop rdi ...[생략]... </pre> <br /> 보시면, "hr" 변수의 값을 "rdi" 레지스터에 보관 후, 마지막에 함수의 스택 프레임을 정리하는 시기에 "pop rdi"를 호출하기까지 값이 변함이 없습니다.<br /> <br /> 따라서, rdi 레지스터에 해당 GC Heap의 주소를 보관하고 있기 때문에 루트(root) 역할을 하게 되고, 따라서 GC 되어서는 안 됩니다. 그런데, 어떻게 이것을 해제해 버리는 걸까요?<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이에 대한 해답을 다음의 글에서 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > When does an object become available for garbage collection? ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20100810-00/?p=13193'>https://devblogs.microsoft.com/oldnewthing/20100810-00/?p=13193</a> </pre> <br /> 위의 글에서는 아래의 예제로 설명을 시작합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class SomeClass { ... string SomeMethod(string s, bool reformulate) { OtherClass o = new OtherClass(s); string result = Frob(o); if (reformulate) Reformulate(); return result; } } </pre> <br /> OtherClass의 인스턴스가 변수 "o"에 저장이 되는데요, 이때의 변수 "o"는 결국 스택의 한 위치에 불과하고 그 스택의 위치는 Frob 메서드가 반환하는 "string result" 변수와 공유되는 것도 가능합니다. 그런 상황을 다음과 같이 어셈블리 코드로 표현하고 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > mov ecx, esi ; load "this" pointer into ecx register mov edx, <span style='color: blue; font-weight: bold'>[ebp-8]</span> ; load parameter ("o") into edx register call SomeClass.Frob ; call method mov <span style='color: blue; font-weight: bold'>[ebp-8]</span>, eax ; re-use memory for "o" as "result" </pre> <br /> 따라서, 변수 "o"에 해당하는 "[ebp - 8]" 스택 위치가 이후 변수 "result"에 해당하는 값을 함께 보관하는 용도로 사용되기 때문에 GC 입장에서는 "o" 인스턴스에 대한 스택의 루트 참조가 "Frob" 메서드의 반환 후 "mov [ebp - 8], eax" 시점에 없어지기 때문에 "o" 인스턴스가 GC 대상이 될 수 있습니다.<br /> <br /> 정리해 보면, JIT 컴파일러가 (Debug가 아닌) release 빌드 환경이라면 저런 스택 메모리 사용의 최적화로 인해 "SomeMethod" 자체의 반환 시점이 아닌, 내부 코드 실행 중인 Frob 메서드의 반환 시점에 이미 "o" 인스턴스는 GC 대상이 될 수 있습니다. 여기까지는 단순한 JIT 컴파일러가 출력한 어셈블리로 인해 충분히 이해될 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> (하지만, 위의 동작 방식에 대한 설명도 여전히 우리가 테스트했던 예제 코드의 "rdi" 레지스터를 무시하는 것에 대한 설명이 안 됩니다.)<br /> <br /> 그런데, 해당 글에서는 이야기가 진행되면서 더 재미있는 설명을 하고 있습니다. 위의 예제 코드에서 "Frob" 메서드가 매개변수로 전달받은 "o" 개체에 대해 사용을 하지 않는다고 가정해보겠습니다. 그렇다면, "o" 개체는 Frob 메서드가 실행을 반환하기 이전에라도 GC 대상으로 포함시켜도 무방합니다.<br /> <br /> 반면, Frob 함수가 아래와 같이 o 인스턴스를 사용한다고 가정해보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string Frob(OtherClass o) { string result = FrobColor(o.GetEffectiveColor()); // ...[생략]... return result; } </pre> <br /> 사용자가 만든 저 코드는, 어쩌면 FrobColor를 반환할 때까지는 o 개체가 GC 대상에서 제외될 수 있다고 판단할 수 있지만, 엄밀히 위의 코드는 다음과 같이 해석이 되기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string Frob(OtherClass o) { var v1 = o.GetEffectiveColor(); string result = FrobColor(v1); } </pre> <br /> o 인스턴스의 사용은 FrobColor 이전에 사용이 완료되었으므로 FrobColor 수행 이전에 GC 대상으로 적합하게 됩니다. 즉, 매개변수로 넘어온 인스턴스도 메서드의 실행 중 어느 시점에는 더 이상 사용하지 않는다는 것을 계산해 넣을 수 있습니다.<br /> <br /> 심지어 GetEffectiveColor가 이렇게 구현되었다고 가정해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Color GetEffectiveColor() { <span style='color: blue; font-weight: bold'>Color color = this.Color;</span> for (OtherClass o = <span style='color: blue; font-weight: bold'>this.Parent</span>; o != null; o = o.Parent) { color = BlendColors(color, o.Color); } return color; } </pre> <br /> 그렇다면, 위의 코드도 "this.Parent"를 한 시점에 "this" 인스턴스의 사용은 모두 완료됐기 때문에 역시 GC 가능한 개체로 볼 수 있습니다.<br /> <br /> 결국 SomeMethod에서 생성한 "o" 개체는 Frob, GetEffectiveColor 메서드를 연이어 호출하면서 사용하고는 있지만, "SomeMethod"의 메서드 반환까지 살아 있을 필요는 없고, GetEffectiveColor의 "this.Parent"를 구한 시점에 GC 대상이 될 수 있다는, 결론을 내릴 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 물론, 여기까지의 설명도 여전히 "rdi"로 유지하고 있는 개체를 어떻게 GC 구성요소가 해제해 버리는가에 대한 답은 아닙니다. 기술적으로 봤을 때, GC 구성요소는 해당 개체를 해제할 수 없습니다.<br /> <br /> 하지만, ^^ 마지막에 이에 대한 해답이 나옵니다. GC 자체로는 그런 능력이 없지만, "JIT 컴파일러"와 협업을 하기 때문에 그것이 가능하다는 점입니다. 즉, 스택이나 레지스터에 등록된 특정 참조를 무시할 수 있도록 JIT 컴파일러가 구성한 메타데이터 정보의 도움을 받는다는 것입니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Even if the reference is left on the stack, the JIT can leave some metadata behind that tells the GC, “If you see the instruction pointer in this range, then ignore the reference in this slot since it’s a dead variable,” similar to how in unmanaged code on non-x86 platforms, metadata is used to guide structured exception handling.<br /> </div><br /> <br /> 결국, 우리가 만들었던 rdi 레지스터의 참조는, JIT 컴파일러가 만들어 놓은 특정 범위에 속하면 "root 개체의 자격"에서 일시적으로 제외되었던 것입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8874
(왼쪽의 숫자를 입력해야 합니다.)