Microsoft MVP성태의 닷넷 이야기
닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock [링크 복사], [링크+제목 복사],
조회: 8920
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)
(시리즈 글이 3개 있습니다.)
디버깅 기술: 92. windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법
; https://www.sysnet.pe.kr/2/0/11268

닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock
; https://www.sysnet.pe.kr/2/0/13552

닷넷: 2215. windbg - thin/fat lock 없이 동작하는 Monitor.Wait + Pulse
; https://www.sysnet.pe.kr/2/0/13553




windbg - Monitor.Enter의 thin lock과 fat lock

마침 아래와 같은 좋은 글이 있어,

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/

Sync Block Index (SBI) \ Object Header Word
; https://yonifedaeli.blogspot.com/2017/03/sync-block-index-sbi-object-header-word.html

소개하려고 합니다. (그대로 베낍니다. ^^)




예를 들어, 다음과 같이 코드를 작성 후,

using System.Threading;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            object o = new object();
            lock (o)
            {
                Debugger.Break();
            }
        }
    }
}

windbg를 이용해 실행한 다음 (처음 한 번은 'g' 키를 눌러 진행해) Debugger.Break에서 멈추었을 때 thinlock 상태를 확인할 수 있습니다.

0:000> !dumpheap -thinlock
         Address               MT     Size
000001d125c02e38 00007fffea220b08       24 ThinLock owner 1 (000001d1241a2970) Recursive 0
Found 1 objects.

0:000> !DumpObj /d 000001d125c02e38
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

thinlock 상태에서는 별도의 SyncBlock이 생성되지 않으므로 sos의 syncblk 명령어를 사용하면 비어있는 것을 볼 수 있습니다.

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
-----------------------------
Total           0
CCW             0
RCW             0
ComClassFactory 0
Free            0

즉, thinlock은 lock이 경합을 벌이지 않는 상태라면 성능을 위해 별도의 SyncBlock을 생성하지 않고 Object Header 내에만 정보를 유지하는 단계인 것입니다. 실제로 이 상태의 해당 object, 위의 소스코드에서는 "o" 인스턴스를 확인해 보면,

0:000> dq 000001d125c02e38-8 L3
000001d1`25c02e30  00000001`00000000 00007fff`ea220b08
000001d1`25c02e40  00000000`00000000

하위 값이 0x00000001로 나오는데, 이 상태의 의미를 확인해 보면,

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

[이미지 출처: "Managed object internals, Part 2. Object header layout and the cost of locking"]
object_header_syncblk_0.png

0~10비트: thread id that’s holding the lock

[이미지 출처: "Managed object internals, Part 2. Object header layout and the cost of locking"]
object_header_syncblk_1.png

해당 lock을 소유하고 있는 스레드 ID가 1로 나오는데 이를 !threads로 알아낼 수 있습니다.

0:009> !threads
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    1 cfa0 000001d1241a2970  202a020 Preemptive  0000025ECD2A65C8:0000025ECD2A7FD0 0000025ecb632c70 0     MTA 
   5    2 bf60 0000015ecb6bcf90    2b220 Preemptive  0000000000000000:0000000000000000 0000025ecb632c70 0     MTA (Finalizer) 

따라서, 특정 lock이 thinlock 상태라면 1) 한 번도 다른 스레드에서 lock 경합을 벌인 적이 없거나, 2) 경합을 벌였어도 일정 spin 타임 내에 있는 경우가 됩니다. (이 외에도, GetHashCode를 부른 적이 없어야 합니다.)

자, 그럼 일부러 경합을 벌여보면,

static void Main(string[] args)
{
    object o = new object();
    lock (o)
    {
        Task.Run(() =>
        {
            // This will promote a thin lock as well
            lock (o) { }
        });

        // 10 ms is not enough, the CLR spins longer than 10 ms.
        Thread.Sleep(100);
        Debugger.Break();
    }
}

이제는 syncblk이 생성된 것을 볼 수 있고,

0:009> !dumpheap -thinlock
         Address               MT     Size
Found 0 objects.

0:000> !syncblk
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

인스턴스 'o'에 대해 Object Header를 조사해 보면,

0:000> dq 000002de10f52e50-8 L3
000002de`10f52e48  08000006`00000000 00007fff`ea220b08
000002de`10f52e58  00000000`00000000

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

[이미지 출처: "Managed object internals, Part 2. Object header layout and the cost of locking"]
object_header_syncblk_2.png

BLT_SBLK_IS_HASH_OR_SYNCBLOCKINDEX 플래그가 1로 설정돼 있으므로 이하 (BIT_SBLK_IS_HASHCODE를 제외한) 0~25 비트의 값이 Sync Block에 대한 인덱스를 가리키게 됩니다. 위의 경우 그 값은 6이고, !syncblk 명령어의 결과에 있는 Index 값에 정확히 일치합니다.




참고로, 예전에 썼던 글의 내용에서,

windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법
; https://www.sysnet.pe.kr/2/0/11268

lock을 대기 중인 스레드의 kv 결과로 어떤 SyncBlock에 걸려 있는지를 알아낼 수 있다고 했는데요, 이번에 다시 해보니,

0:009> kv
 # 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 000002de`0f4a8838 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     : 000002de`0f4a8838 00007fff`f02c735b 000002de`0f4a8838 000002de`10f52e50 : clr!AwareLock::EnterEpilogHelper+0x11a
07 000000f4`222feba0 00007fff`f05ee5a9     : 000002de`296f3860 000002de`0f4a8838 00000000`00000000 000000f4`222fedf0 : clr!AwareLock::EnterEpilog+0x5a
...[생략]...

CLREventBase::WaitEx에는 값이 안 나오고, AwareLock::EnterEpilogHelper 등의 "Args to Child"에서 확인할 수 있었습니다. (이 값은 닷넷 패치 버전에 따라서도 달라질 수 있기 때문에 항상 그렇다고 가정해서는 안 됩니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/14/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  [127]  128  129  130  131  132  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
2879정성태3/3/201526674개발 환경 구성: 259. Visual Studio 없이 Visual C++ 컴파일하는 방법
2878정성태2/28/201527516.NET Framework: 503. == 연산자보다는 Equals 메서드의 호출이 더 권장됩니다. [3]파일 다운로드1
2877정성태2/28/201521740.NET Framework: 502. 연산자 재정의(operator overloading)와 메서드 재정의(method overriding)의 다른 점 - 가상 함수 호출 여부 [3]파일 다운로드1
2876정성태2/27/201524214VS.NET IDE: 98. IntegraStudio - Visual Studio에서 Java 프로그램 개발
2875정성태2/26/201522812디버깅 기술: 72. Visual Studio 2013에서의 sos.dll 사용 제한
2874정성태2/26/201519570디버깅 기술: 71. windbg + 닷넷 디버깅 (2) - null 체크 패턴
2873정성태2/25/201537034.NET Framework: 501. FtpWebRequest 타입을 이용해 FTP 파일 업로드 [4]파일 다운로드1
2872정성태2/25/201521206디버깅 기술: 70. windbg + 닷넷 디버깅 (1) - 배열 인덱스 사용 패턴
2871정성태2/24/201525200개발 환경 구성: 258. 윈도우 8.1에서 방화벽과 함께 FTP 서버 여는 (하지만, 권장하지 않는) 방법 [1]
2870정성태2/24/201526312개발 환경 구성: 257. 윈도우 8.1에서 방화벽과 함께 FTP 서버 여는 방법
2869정성태2/23/201520307.NET Framework: 500. struct로 정의한 값 형식(Value Type)의 경우 Equals 재정의를 권장합니다.파일 다운로드1
2868정성태2/23/201524804VS.NET IDE: 97. Visual C++ 프로젝트 디버깅 시에 Step-Into(F11) 동작이 원치 않는 함수로 진입하는 것을 막는 방법 [2]
2867정성태2/23/201518485오류 유형: 273. File History - Failed to initiate user data backup (error 80070005)
2866정성태2/23/201520327오류 유형: 272. WAT080 : Failed to locate the Windows Azure SDK. Please make sure the Windows Azure SDK v2.1 is installed.
1868정성태2/20/201517623오류 유형: 271. The type '...' cannot be used as type parameter 'TContext' in the generic type or method 'System.ServiceModel.DomainServices.EntityFramework.LinqToEntitiesDomainService<T>
1866정성태2/20/201518532오류 유형: 270. "aspnet_regiis -i" 실행 시 0x00000006 오류 해결 방법
1865정성태2/20/201519840.NET Framework: 499. 특정 닷넷 프레임워크 버전 이후부터 제공되는 타입을 사용해야 한다면?
1864정성태2/18/201524832.NET Framework: 498. C#으로 간단하게 만들어 본 ASCII Art 프로그램 [2]파일 다운로드1
1862정성태2/18/201528675.NET Framework: 497. .NET Garbage Collection에 대한 정리 [6]
1861정성태2/18/201524034.NET Framework: 496. 마우스 커서가 놓인 지점의 문자열 얻는 방법 [1]파일 다운로드1
1860정성태2/18/201523865.NET Framework: 495. CorElementType의 요소 값 설명파일 다운로드1
1859정성태2/17/201524253Windows: 106. 컴퓨터를 재부팅하면 절전(Power Saver) 전원 모드로 돌아가는 경우
1858정성태2/16/201534263Windows: 105. 자동으로 로그아웃/잠김 화면 상태로 전환된다면? [2]
1857정성태2/16/201522268.NET Framework: 494. 값(struct) 형식의 제네릭(Generic) 타입이 박싱되는 경우의 메타데이터 토큰 값파일 다운로드1
1856정성태2/15/201521251.NET Framework: 493. TypeRef 메타테이블에 등록되는 타입의 조건파일 다운로드1
1855정성태2/10/201520798개발 환경 구성: 256. WebDAV Redirector - Sysinternals 폴더 연결 시 "The network path was not found" 오류 해결 방법
... 121  122  123  124  125  126  [127]  128  129  130  131  132  133  134  135  ...