성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
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'>.NET 참조 개체 인스턴스의 Object Header를 확인하는 방법</h1> <p> 사실, 원래는 StringBuilder에서 OutOfMemoryException의 원인만 밝히려고 했는데 쓰다 보니 궁금함이 꼬리를 물어서 이렇게 이야기가 길어졌군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > StringBuilder에서의 OutOfMemoryException 오류 원인 분석 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1171'>http://www.sysnet.pe.kr/2/0/1171</a> windbg - 힙에서 .NET 타입에 대한 배열을 찾는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1172'>http://www.sysnet.pe.kr/2/0/1172</a> .NET Array는 왜 12bytes의 기본 메모리를 점유할까? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1173'>http://www.sysnet.pe.kr/2/0/1173</a> 일반 참조형의 메모리 소비는 얼마나 될까요? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1174'>http://www.sysnet.pe.kr/2/0/1174</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;' > Safe Thread Synchronization ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2003/january/net-column-safe-thread-synchronization'>https://learn.microsoft.com/en-us/archive/msdn-magazine/2003/january/net-column-safe-thread-synchronization</a> </pre> <br /> 위의 글에 보면, Managed Heap에서의 개체 메모리에 대한 정의가 다음과 같이 나옵니다.<br /> <br /> <img alt='syncblock_index_1.png' src='/SysWebRes/bbs/syncblock_index_1.png' /><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;' > === ConsoleApplication1.Program 인스턴스의 메모리 덤프 === 0:004> dd 026cbf90 026cbf90 <span style='color: blue; font-weight: bold'>00293810 00000000 00000000</span> 00000000 026cbfa0 00000000 00000000 00000000 00000000 </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;' > 00293810 == MethodTable Pointer 00000000 == SyncBlock Index 00000000 == Object Fields </pre> <br /> 실제로는 3개의 공간이긴 해도 저런 식이 아니고 -4(64비트에서는 -8)바이트 위치에서 3개의 공간이 점유되어 다음과 같은 의미를 갖게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> dd 026CBF8C 026CBF8C <span style='color: blue; font-weight: bold'>00000000 00293810 00000000</span> 00000000 026CBF9C 00000000 00000000 00000000 00000000 00000000 == Object Header (SyncBlock Index) 00293810 == MethodTable Pointer 00000000 == 핃드가 없어도 최소 1개의 공간을 점유 </pre> <br /> 그리고 Object Header가 담고 있는 SyncBlock Index의 개념은 다음의 글에서 자세하게 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/may/net-framework-internals-how-the-clr-creates-runtime-objects'>https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/may/net-framework-internals-how-the-clr-creates-runtime-objects</a> </pre> <br /><a name="n2img" /> <img alt='syncblock_index_2.png' src='/SysWebRes/bbs/syncblock_index_2.png' /><br /> <br /> 재미있는 것은, SyncBlock Index의 기본값이 아마도 CLR 버전에 따라 달라지는 것 같습니다.<br /> <br /> "<a target='tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2003/january/net-column-safe-thread-synchronization'>Safe Thread Synchronization</a>" 글에서는 다음과 같이 lock이 걸리지 않은 상태에서는 -1 값으로 채워져 있다는 설명을 포함하고 있는데,<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'> "<br /> On the other hand, ObjectB's SyncBlockIndex field is set to -1 indicating that ObjectB doesn't have a SyncBlock associated with it for its use<br /> "<br /> </div><br /> <br /> "<a target='tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/may/net-framework-internals-how-the-clr-creates-runtime-objects'>Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects</a>" 글에서는 0 값으로 채워져 있다고 되어 있습니다.<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'> "<br /> For most object instances, there will be no storage allocated for the actual SyncBlock and the syncblk number will be zero<br /> "<br /> </div><br /> <br /> 현재 버전에서 어느 쪽이 맞는지, 한번 확인을 해볼까요? ^^<br /> <br /> 비교를 위해 lock이 걸린 object와 그렇지 않았을 때의 메모리 구조를 확인해야 할 텐데요. 분석을 쉽게 하기 위해 다음과 같이 간단하게 코딩을 하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Program[] pgList = new Program[0]; Thread.Sleep(-1); } } } </pre> <br /> windbg로 메모리를 덤프해 보면,<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'>!do 0228bf90</span> Name: System.Object[] MethodTable: 72f86ba8 EEClass: 72d09688 <span style='color: blue; font-weight: bold'>Size: 16(0x10) bytes</span> Array: Rank 1, N<span style='color: blue; font-weight: bold'>umber of elements 0</span>, Type CLASS Element Type:ConsoleApplication1.Program Fields: None 0:000> <span style='color: blue; font-weight: bold'>dd 0228bf8c</span> (0x0228bf90 - 4 == 0228bf8c) 0228bf8c <span style='color: blue; font-weight: bold'>00000000 72f86ba8 00000000 00163810</span> 0228bf9c 00000000 00000000 00000000 00000000 0228bfac 00000000 00000000 00000000 00000000 </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;' > 00000000 == SyncBlock Index 72f86ba8 == MethodTable (System.Object []) 00000000 == 배열의 수 00163810 == 배열 요소의 Type에 대한 MethodTable </pre> <br /> 아하... 사용되지 않는 SyncBlock Index의 값은 0이 맞군요. ^^ (각각의 글이 씌여진 시점에 따라 .NET의 버전 차이라고 의심되었으나, .NET 1.1/2.0/4.0 응용 프로그램에 대해서 동일한 결과값을 얻었습니다.)<br /> <br /> 본론으로 돌아가서, 이제 해당 object 에 대해 lock을 걸어서 컴파일 한 다음,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Program[] pgList = new Program[0]; lock (pgList) { Thread.Sleep(-1); } } } } </pre> <br /> windbg로 메모리를 덤프해 보면,<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'>!do 026dbf90 </span> Name: System.Object[] MethodTable: 72f86ba8 EEClass: 72d09688 Size: <span style='color: blue; font-weight: bold'>16(0x10) bytes</span> Array: Rank 1, <span style='color: blue; font-weight: bold'>Number of elements 0</span>, Type CLASS Element Type:ConsoleApplication1.Program Fields: None <span style='color: blue; font-weight: bold'>ThinLock owner 1 (0077e0a8), Recursive 0</span> 0:000> <span style='color: blue; font-weight: bold'>dd 026dbf8c</span> (026dbf90 - 4 == 026dbf8c) 026dbf8c <span style='color: blue; font-weight: bold'>00000001 72f86ba8 00000000 00283810</span> 026dbf9c 00000000 00000000 00000000 00000000 026dbfac 00000000 00000000 00000000 00000000 </pre> <br /> 이제는 SyncBlock Index 값이 1로 설정되어 있고 !DumpObject의 결과에는 "ThinLock owner 1 (0077e0a8), Recursive 0"이라는 출력 결과도 확인할 수 있습니다.<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'>00000001 == SyncBlock Index</span> 72f86ba8 == MethodTable (System.Object []) 00000000 == 배열의 수 00283810 == 배열 요소의 Type에 대한 MethodTable </pre> <br /> <hr style='width: 50%' /><br /> <br /> '참조형'의 값은 모두 이렇게 데이터 구조에 "Object Header"를 가지고 있는 반면, 스택에 생성되는 '값형식'에서는 "Object Header"가 존재하지 않습니다.<br /> <br /> 바로 이 때문에 참조값에 대해서만 닷넷에서 lock 구문(Monitor.Enter 메서드)으로 사용할 수 있는 것입니다.<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'>object lockInstance = new object();</span> lock (lockInstance) // 이 시점에 Object Header로 SyncBlock Index 값이 설정되고, { } // 이 시점에 SyncBlock == 0x00000000으로 해제됨. try { Monitor.Enter(lockInstance); // 이 시점에 Object Header로 SyncBlock Index 값이 설정되고, } finally { Monitor.Exit(lockInstance); // 이 시점에 SyncBlockIndex == 0x00000000으로 해제됨. } <span style='color: blue; font-weight: bold'>int errorLock = 5;</span> lock (errorLock) // 컴파일 오류: 'int' is not a reference type as required by the lock statement { } </pre> <br /> 때로는 문서에서만 읽었던 이런 내용들을, 직접 확인해 보는 것도 그런대로 재미가 있군요. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1928
(왼쪽의 숫자를 입력해야 합니다.)