성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
글쓰기
제목
이름
암호
전자우편
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'>windbg - Monitor.Enter의 thin lock과 fat lock</h1> <p> 마침 아래와 같은 좋은 글이 있어,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Managed object internals, Part 2. Object header layout and the cost of locking ; <a target='tab' href='https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-2-object-header-layout-and-the-cost-of-locking/'>https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-2-object-header-layout-and-the-cost-of-locking/</a> Sync Block Index (SBI) \ Object Header Word ; <a target='tab' href='https://yonifedaeli.blogspot.com/2017/03/sync-block-index-sbi-object-header-word.html'>https://yonifedaeli.blogspot.com/2017/03/sync-block-index-sbi-object-header-word.html</a> </pre> <br /> 소개하려고 합니다. (그대로 베낍니다. ^^)<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;' > using System.Threading; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { object o = new object(); <span style='color: blue; font-weight: bold'>lock (o)</span> { Debugger.Break(); } } } } </pre> <br /> windbg를 이용해 실행한 다음 (처음 한 번은 'g' 키를 눌러 진행해) Debugger.Break에서 멈추었을 때 thinlock 상태를 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!dumpheap -thinlock</span> Address MT Size <span style='color: blue; font-weight: bold'>000001d125c02e38</span> 00007fffea220b08 24 ThinLock owner 1 (000001d1241a2970) Recursive 0 Found 1 objects. 0:000> <span style='color: blue; font-weight: bold'>!DumpObj /d 000001d125c02e38</span> Name: System.Object MethodTable: 00007fffea220b08 EEClass: 00007fffea1d48f0 Size: 24(0x18) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Object Fields: None ThinLock owner 1 (000001d1241a2970), Recursive 0 </pre> <br /> thinlock 상태에서는 별도의 SyncBlock이 생성되지 않으므로 sos의 syncblk 명령어를 사용하면 비어있는 것을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!syncblk</span> Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner ----------------------------- Total 0 CCW 0 RCW 0 ComClassFactory 0 Free 0 </pre> <br /> 즉, thinlock은 lock이 경합을 벌이지 않는 상태라면 성능을 위해 별도의 SyncBlock을 생성하지 않고 Object Header 내에만 정보를 유지하는 단계인 것입니다. 실제로 이 상태의 해당 object, 위의 소스코드에서는 "o" 인스턴스를 확인해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dq 000001d125c02e38-8 L3</span> 000001d1`25c02e30 <span style='color: blue; font-weight: bold'>00000001</span>`00000000 00007fff`ea220b08 000001d1`25c02e40 00000000`00000000 </pre> <br /> 하위 값이 0x00000001로 나오는데, 이 상태의 의미를 확인해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0000 0000 0000 0000 0000 0000 0000 0001 |||| ||- BIT_SBLK_IS_HASHCODE |||| |- BLT_SBLK_IS_HASH_OR_SYNCBLOCKINDEX ||||-BIT_SBLK_SPIN_LOCK |||- BIT_SBLK_GC_RESERVE ||- BIT_SBLK_FINALIZER_RUN |- BIT_SBLK_AGILE_IN_PROGRESS [이미지 출처: "<a target='tab' href='https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-2-object-header-layout-and-the-cost-of-locking/'>Managed object internals, Part 2. Object header layout and the cost of locking</a>"] <img onclick='toggle_img(this)' class='imgView' alt='object_header_syncblk_0.png' src='/SysWebRes/bbs/object_header_syncblk_0.png' /> 0~10비트: thread id that’s holding the lock [이미지 출처: "<a target='tab' href='https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-2-object-header-layout-and-the-cost-of-locking/'>Managed object internals, Part 2. Object header layout and the cost of locking</a>"] <img onclick='toggle_img(this)' class='imgView' alt='object_header_syncblk_1.png' src='/SysWebRes/bbs/object_header_syncblk_1.png' /> </pre> <br /> 해당 lock을 소유하고 있는 스레드 ID가 1로 나오는데 이를 !threads로 알아낼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:009> <span style='color: blue; font-weight: bold'>!threads</span> ThreadCount: 4 UnstartedThread: 0 BackgroundThread: 3 PendingThread: 0 DeadThread: 0 Hosted Runtime: no Lock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 <span style='color: blue; font-weight: bold'>1 cfa0 000001d1241a2970</span> 202a020 Preemptive 0000025ECD2A65C8:0000025ECD2A7FD0 0000025ecb632c70 0 MTA 5 2 bf60 0000015ecb6bcf90 2b220 Preemptive 0000000000000000:0000000000000000 0000025ecb632c70 0 MTA (Finalizer) </pre> <br /> 따라서, 특정 lock이 thinlock 상태라면 1) 한 번도 다른 스레드에서 lock 경합을 벌인 적이 없거나, 2) 경합을 벌였어도 일정 spin 타임 내에 있는 경우가 됩니다. (이 외에도, GetHashCode를 부른 적이 없어야 합니다.)<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 o = new object(); <span style='color: blue; font-weight: bold'>lock (o)</span> { Task.Run(() => { // This will promote a thin lock as well <span style='color: blue; font-weight: bold'>lock (o) { }</span> }); // 10 ms is not enough, the CLR spins longer than 10 ms. Thread.Sleep(100); Debugger.Break(); } } </pre> <br /> 이제는 syncblk이 생성된 것을 볼 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:009> <span style='color: blue; font-weight: bold'>!dumpheap -thinlock</span> Address MT Size Found 0 objects. 0:000> <span style='color: blue; font-weight: bold'>!syncblk</span> Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner 6 000002de0f4a8838 3 1 000002de0f452980 7e48 0 000002de10f52e50 System.Object ----------------------------- Total 6 CCW 1 RCW 2 ComClassFactory 0 Free 0 </pre> <br /> 인스턴스 'o'에 대해 Object Header를 조사해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dq 000002de10f52e50-8 L3</span> 000002de`10f52e48 08000006`00000000 00007fff`ea220b08 000002de`10f52e58 00000000`00000000 </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0x08000006 0000 1000 0000 0000 0000 0000 0000 0110 |||| ||- BIT_SBLK_IS_HASHCODE |||| |- BLT_SBLK_IS_HASH_OR_SYNCBLOCKINDEX (== 0x08000000) ||||-BIT_SBLK_SPIN_LOCK |||- BIT_SBLK_GC_RESERVE ||- BIT_SBLK_FINALIZER_RUN |- BIT_SBLK_AGILE_IN_PROGRESS [이미지 출처: "<a target='tab' href='https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-2-object-header-layout-and-the-cost-of-locking/'>Managed object internals, Part 2. Object header layout and the cost of locking</a>"] <img onclick='toggle_img(this)' class='imgView' alt='object_header_syncblk_2.png' src='/SysWebRes/bbs/object_header_syncblk_2.png' /> </pre> <br /> BLT_SBLK_IS_HASH_OR_SYNCBLOCKINDEX 플래그가 1로 설정돼 있으므로 이하 (BIT_SBLK_IS_HASHCODE를 제외한) 0~25 비트의 값이 Sync Block에 대한 인덱스를 가리키게 됩니다. 위의 경우 그 값은 6이고, !syncblk 명령어의 결과에 있는 Index 값에 정확히 일치합니다.<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;' > windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11268'>https://www.sysnet.pe.kr/2/0/11268</a> </pre> <br /> lock을 대기 중인 스레드의 kv 결과로 어떤 SyncBlock에 걸려 있는지를 알아낼 수 있다고 했는데요, 이번에 다시 해보니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:009> <span style='color: blue; font-weight: bold'>kv</span> # Child-SP RetAddr : Args to Child : Call Site 00 000000f4`222fe5b8 00007ff8`0a08f6f9 : 000002de`296fa3d0 000002de`296fa3e0 000002de`0f3c0000 00007ff8`0c9f6f52 : ntdll!NtWaitForMultipleObjects+0x14 01 000000f4`222fe5c0 00007fff`f0311636 : 00000000`00000000 00007fff`00000000 00000000`ffffffff 000002de`0f4a8850 : KERNELBASE!WaitForMultipleObjectsEx+0xe9 02 000000f4`222fe8a0 00007fff`f031102a : 00000000`00000001 00000000`00000001 00000000`00000000 00007ff8`0c92ab11 : clr!WaitForMultipleObjectsEx_SO_TOLERANT+0x62 03 000000f4`222fe900 00007fff`f0310e01 : 00007fff`00000001 00007fff`00000001 00000000`00000001 00007fff`00000000 : clr!Thread::DoAppropriateWaitWorker+0x206 04 000000f4`222fe9f0 00007fff`f0283157 : 000002de`0f4a8850 00000000`00000001 <span style='color: blue; font-weight: bold'>000002de`0f4a8838</span> 000002de`10f52e00 : clr!Thread::DoAppropriateWait+0x7d 05 000000f4`222fea70 00007fff`f06d93ee : 000000f4`222fedf0 00000000`00000000 000002de`296f3860 00007fff`ea221740 : clr!CLREventBase::WaitEx+0xab 06 000000f4`222feae0 00007fff`f06d92a2 : <span style='color: blue; font-weight: bold'>000002de`0f4a8838</span> 00007fff`f02c735b <span style='color: blue; font-weight: bold'>000002de`0f4a8838</span> 000002de`10f52e50 : clr!AwareLock::EnterEpilogHelper+0x11a 07 000000f4`222feba0 00007fff`f05ee5a9 : 000002de`296f3860 <span style='color: blue; font-weight: bold'>000002de`0f4a8838</span> 00000000`00000000 000000f4`222fedf0 : clr!AwareLock::EnterEpilog+0x5a ...[생략]... </pre> <br /> CLREventBase::WaitEx에는 값이 안 나오고, AwareLock::EnterEpilogHelper 등의 "Args to Child"에서 확인할 수 있었습니다. (이 값은 닷넷 패치 버전에 따라서도 달라질 수 있기 때문에 항상 그렇다고 가정해서는 안 됩니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
7526
(왼쪽의 숫자를 입력해야 합니다.)