http://blog.naver.com/techshare/100143525064
위의 글을 퍼온글입니다.
그런데, 여기서 옛날에 읽었던 글이 하나 더 생각나는 바람에... ^^;
위의 글에 보면, Managed Heap 에서의 개체 메모리에 대한 정의가 다음과 같이 나옵니다.
이에 비춰서 지난 번에 개체들의 메모리를 살펴봤을 때의 덤프 내용을 다시 한번 살펴볼까요?
그림과 매치시켜 보면 아래와 같이 정의되는데요.
물론, 위와 같이 매핑되지 않습니다. 실제로 우리가 테스트해 본 바에 의하면 다음과 같은 의미였기 때문입니다. (아마도, 위의 그림은 단순한 개념도라고 봐야 하지 않을까 싶습니다.)
그렇다면 SyncBlock Index 값은 어디에 저장이 되는 것일까요? 이에 대해서는 다른 글에 답이 있었습니다.
"MT" 값 이전의 -4 bytes 영역에 저장되어 있다고 표현되어 있습니다. 재미있는 것은, SyncBlock Index의 기본값에 대한 설명도 차이가 납니다.
"Safe Thread Synchronization" 글에서는 다음과 같이 lock 이 걸리지 않은 상태에서는 -1 값으로 채워져 있다는 설명을 포함하고 있는데,
"Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects" 글에서는 0 값으로 채워져 있다고 되어 있습니다.
어느 쪽이 맞는지, 한번 확인을 해볼까요? ^^
비교를 위해 lock 이 걸린 object 와 그렇지 않았을 때의 메모리 구조를 확인해야 할 텐데요. 분석을 쉽게 하기 위해 다음과 같이 간단하게 코딩을 하고,
windbg 로 메모리를 덤프해 보면,
덤프된 결과를 각각 다음과 같이 분석할 수 있습니다.
아하... 사용되지 않는 SyncBlock Index의 값은 0 이 맞군요. ^^ (각각의 글이 씌여진 시점에 따라 .NET 의 버전차이라고 의심되었으나, .NET 1.1/2.0/4.0 응용 프로그램에 대해서 동일한 결과값을 얻었습니다.)
본론으로 돌아가서, 이제 해당 object 에 대해 lock 을 걸어서 컴파일 한 다음,
windbg 로 메모리를 덤프해 보면,
이제는 SyncBlock Index 값이 1 로 설정되어 있고 !DumpObject 의 결과에는 "ThinLock owner 1 (0077e0a8), Recursive 0" 이라는 출력결과도 확인할 수 있습니다.
'참조형'의 값은 모두 이렇게 데이터 구조에 "SyncBlock"을 가지고 있는 반면, 스택에 생성되는 '값형식' 에서는 "SyncBlock" 이 존재하지 않습니다.
바로 이 때문에 참조값에 대해서만 닷넷에서 lock 구문(Monitor.Enter 메서드)으로 사용할 수 있는 것입니다.
때로는 문서에서만 읽었던 이런 내용들을, 직접 확인해 보는 것도 그런데로 재미가 있군요. ^^
여기서부터 저의 주석 쩝 syncblock에 대한 내용을 내 블로그에서 다뤘었는데 data structure까지는 미처 다루지 못했었습니다. 사실 잘 몰랐구요 참 위의 블로그에서 많은 걸 배웁니다. 껄껄껄 정성태씨 하이팅
위의 글을 퍼온글입니다.
사실, 원래는 StringBuilder 에서 OutOfMemoryException 의 원인만 밝히려고 했는데 쓰다 보니 궁금함이 꼬리를 물어서 이렇게 이야기가 길어졌군요. ^^
StringBuilder 에서의 OutOfMemoryException 오류 원인 분석 ; http://www.sysnet.pe.kr/2/0/1171 windbg - 힙에서 .NET 타입에 대한 배열을 찾는 방법 ; http://www.sysnet.pe.kr/2/0/1172 .NET Array는 왜 12 bytes 의 기본 메모리를 점유할까? ; http://www.sysnet.pe.kr/2/0/1173 일반 참조형의 메모리 소비는 얼마나 될까요? ; http://www.sysnet.pe.kr/2/0/1174
그런데, 여기서 옛날에 읽었던 글이 하나 더 생각나는 바람에... ^^;
Safe Thread Synchronization ; http://msdn.microsoft.com/en-us/magazine/cc188793.aspx
위의 글에 보면, Managed Heap 에서의 개체 메모리에 대한 정의가 다음과 같이 나옵니다.
이에 비춰서 지난 번에 개체들의 메모리를 살펴봤을 때의 덤프 내용을 다시 한번 살펴볼까요?
=== ConsoleApplication1.Program 인스턴스의 메모리 덤프 ===
0:004> dd 026cbf90
026cbf90 00293810 00000000 00000000 00000000
026cbfa0 00000000 00000000 00000000 00000000
그림과 매치시켜 보면 아래와 같이 정의되는데요.
00293810 == MethodTable Pointer 00000000 == SyncBlock Index 00000000 == Object Fields
물론, 위와 같이 매핑되지 않습니다. 실제로 우리가 테스트해 본 바에 의하면 다음과 같은 의미였기 때문입니다. (아마도, 위의 그림은 단순한 개념도라고 봐야 하지 않을까 싶습니다.)
00293810 == MethodTable Pointer 00000000 == 여분(?) 00000000 == [unknown]
그렇다면 SyncBlock Index 값은 어디에 저장이 되는 것일까요? 이에 대해서는 다른 글에 답이 있었습니다.
Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects ; http://msdn.microsoft.com/en-us/magazine/cc163791.aspx
"MT" 값 이전의 -4 bytes 영역에 저장되어 있다고 표현되어 있습니다. 재미있는 것은, SyncBlock Index의 기본값에 대한 설명도 차이가 납니다.
"Safe Thread Synchronization" 글에서는 다음과 같이 lock 이 걸리지 않은 상태에서는 -1 값으로 채워져 있다는 설명을 포함하고 있는데,
"
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
"
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
"
"Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects" 글에서는 0 값으로 채워져 있다고 되어 있습니다.
"
For most object instances, there will be no storage allocated for the actual SyncBlock and the syncblk number will be zero
"
For most object instances, there will be no storage allocated for the actual SyncBlock and the syncblk number will be zero
"
어느 쪽이 맞는지, 한번 확인을 해볼까요? ^^
비교를 위해 lock 이 걸린 object 와 그렇지 않았을 때의 메모리 구조를 확인해야 할 텐데요. 분석을 쉽게 하기 위해 다음과 같이 간단하게 코딩을 하고,
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Program[] pgList = new Program[0]; Thread.Sleep(-1); } } }
windbg 로 메모리를 덤프해 보면,
// 이전 글들의 예제에서 많이 포함했으므로, 이전 단계는 생략합니다. 0:000> !do 0228bf90 Name: System.Object[] MethodTable: 72f86ba8 EEClass: 72d09688 Size: 16(0x10) bytes Array: Rank 1, Number of elements 0, Type CLASS Element Type:ConsoleApplication1.Program Fields: None 0:000> dd 0228bf8c (0x0228bf90 - 4 == 0228bf8c) 0228bf8c 00000000 72f86ba8 00000000 00163810 0228bf9c 00000000 00000000 00000000 00000000 0228bfac 00000000 00000000 00000000 00000000
덤프된 결과를 각각 다음과 같이 분석할 수 있습니다.
00000000 == SyncBlock Index 72f86ba8 == MethodTable (System.Object []) 00000000 == 배열의 수 00163810 == System.Object[] 에 담긴 요소의 Type에 대한 MethodTable 00000000 == ?
아하... 사용되지 않는 SyncBlock Index의 값은 0 이 맞군요. ^^ (각각의 글이 씌여진 시점에 따라 .NET 의 버전차이라고 의심되었으나, .NET 1.1/2.0/4.0 응용 프로그램에 대해서 동일한 결과값을 얻었습니다.)
본론으로 돌아가서, 이제 해당 object 에 대해 lock 을 걸어서 컴파일 한 다음,
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Program[] pgList = new Program[0]; lock (pgList) { Thread.Sleep(-1); } } } }
windbg 로 메모리를 덤프해 보면,
0:000> !do 026dbf90 Name: System.Object[] MethodTable: 72f86ba8 EEClass: 72d09688 Size: 16(0x10) bytes Array: Rank 1, Number of elements 0, Type CLASS Element Type:ConsoleApplication1.Program Fields: None ThinLock owner 1 (0077e0a8), Recursive 0 0:000> dd 026dbf8c (026dbf90 - 4 == 026dbf8c) 026dbf8c 00000001 72f86ba8 00000000 00283810 026dbf9c 00000000 00000000 00000000 00000000 026dbfac 00000000 00000000 00000000 00000000
이제는 SyncBlock Index 값이 1 로 설정되어 있고 !DumpObject 의 결과에는 "ThinLock owner 1 (0077e0a8), Recursive 0" 이라는 출력결과도 확인할 수 있습니다.
00000001 == SyncBlock Index
72f86ba8 == MethodTable (System.Object [])
00000000 == 배열의 수
00283810 == System.Object[] 에 담긴 요소의 Type에 대한 MethodTable
00000000 == ?
'참조형'의 값은 모두 이렇게 데이터 구조에 "SyncBlock"을 가지고 있는 반면, 스택에 생성되는 '값형식' 에서는 "SyncBlock" 이 존재하지 않습니다.
바로 이 때문에 참조값에 대해서만 닷넷에서 lock 구문(Monitor.Enter 메서드)으로 사용할 수 있는 것입니다.
object lockInstance = new object(); lock (lockInstance) // 이 시점에 SyncBlock Index 값이 설정되고, { } // 이 시점에 SyncBlock == 0x00000000 으로 해제됨. try { Monitor.Enter(lockInstance); // 이 시점에 SyncBlock Index 값이 설정되고, } finally { Monitor.Exit(lockInstance); // 이 시점에 SyncBlock == 0x00000000 으로 해제됨. } int errorLock = 5; lock (errorLock) // 컴파일 오류: 'int' is not a reference type as required by the lock statement { }
때로는 문서에서만 읽었던 이런 내용들을, 직접 확인해 보는 것도 그런데로 재미가 있군요. ^^
여기서부터 저의 주석 쩝 syncblock에 대한 내용을 내 블로그에서 다뤘었는데 data structure까지는 미처 다루지 못했었습니다. 사실 잘 몰랐구요 참 위의 블로그에서 많은 걸 배웁니다. 껄껄껄 정성태씨 하이팅