Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

windbg - SOS DumpMT의 "BaseSize", "ComponentSize" 값에 대한 의미

일례로 DumpMT를 통해 object [] 타입을 검사하면 다음과 같은 내용을 보게 됩니다.

0:000> !DumpMT 00007ff9624f6fc0
EEClass:         00007ff961ebaa00
Module:          00007ff961df1000
Name:            System.Object[]
mdToken:         0000000002000000
File:            C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
BaseSize:        0x18
ComponentSize:   0x8
Slots in VTable: 28
Number of IFaces in IFaceMap: 6

가만 보니 대부분의 타입은 ComponentSize가 8로 나옵니다. 검색으로 찾아봐도 딱히 설명이 없군요. 그래서 이번에도 지난번에 했던 것처럼,

windbg - SOS DumpClass/DumpMT의 "Vtable Slots", "Total Method Slots", "Slots in VTable" 값에 대한 의미
; https://www.sysnet.pe.kr/2/0/11337

제 나름대로 규칙을 정리해 봤습니다. ^^




우선, ComponentSize는 해당 객체를 표현하는 메모리 구조에서 "요소의 수"가 있는 경우에만 1 이상의 값을 갖습니다. 즉, "요소"가 하나 늘어날 때마다 증가하는 메모리의 바이트를 나타내는 것입니다. 이런 유형의 타입으로 바로 배열이 있습니다.

.NET Array는 왜 12bytes의 기본 메모리를 점유할까?
; https://www.sysnet.pe.kr/2/0/1173

* 위의 경우는 x86에서 테스트했기 때문에 12(0xc)바이트 기본 점유이지만, x64에서는 24(0x18)바이트입니다.

그리고 BaseSize는 요소를 하나도 갖고 있지 않은 경우에도 기본적으로 차지하는 메모리를 의미합니다. 따라서 다음과 같은 공식으로 정리할 수 있습니다.

(배열) 객체의 크기 = BaseSize + (# of elements * ComponentSize)

확인을 해볼까요? ^^

우선, char 배열로 그 수가 0, 1, 2인 배열에 대해서 보겠습니다.

new char[0]

위의 객체를 dumpobj로 출력하면 그 크기가 나옵니다.

0:000> !DumpObj /d 000001d312232e60
Name:        System.Char[]
MethodTable: 00007ff9624f7a98
EEClass:     00007ff961ebabc0
Size:        24(0x18) bytes
Array:       Rank 1, Number of elements 0, Type Char (Print Array)
Content:     
Fields:
None

요소의 수가 하나도 없는데도 0x18 바이트를 차지하는 것을 보니 아마도 char[] 타입의 BaseSize는 0x18일 것입니다. DumpMT로 확인해 보면 예상대로입니다.

0:000> !DumpMT /d 00007ff9624f7a98
EEClass:         00007ff961ebabc0
Module:          00007ff961df1000
Name:            System.Char[]
mdToken:         0000000002000000
File:            C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
BaseSize:        0x18
ComponentSize:   0x2
Slots in VTable: 28
Number of IFaces in IFaceMap: 6

이에 기반을 둬서 char[1], char[2] 배열의 값을 자동으로 다음과 같이 계산할 수 있습니다.

char[1] = BaseSize 0x18 + (# of elements * ComponentSize 0x2) = 0x1a
char[2] = BaseSize 0x18 + (# of elements * ComponentSize 0x2) = 0x1c

(실제 크기는 DumpObj로 확인할 수 있고 위의 값이 맞습니다.)

byte [] 배열에 대해서도 마찬가지입니다. byte 요소의 크기가 1이므로 이럴 때는 다음과 같이 크기가 계산됩니다.

byte[0] = BaseSize 0x18 + 0 * 1 = 0x18
byte[1] = BaseSize 0x18 + 1 * 1 = 0x19
byte[2] = BaseSize 0x18 + 2 * 1 = 0x1a

그렇다면 값 형식의 경우 다음과 같은 공식으로 일반화할 수 있습니다.

[요소가 값 형식의 경우]

(배열) 객체의 크기 = BaseSize + (# of elements * sizeof(element))

실제로 다음과 같이 struct로 새로운 값 형식을 만들어,

public struct ST
{
    int _i;
    int _j;
    int _k;
}

공식을 확인할 수 있습니다.

ComponentSize = sizeof(ST) = 0xc

ST[0] = BaseSize 0x18 + 0 * 0xc = 0x18
ST[1] = BaseSize 0x18 + 1 * 0xc = 0x24
ST[2] = BaseSize 0x18 + 2 * 0xc = 0x30

그렇다면 참조 형식은 어떨까요? 참조 형식의 경우 배열 내에 참조 값에 대한 주소만 보관하고 있기 때문에 ComponentSize는 언제나 포인터 크기가 됩니다.

[요소가 참조 형식의 경우]

(배열) 객체의 크기 = BaseSize + (# of elements * 8) // 64비트인 경우
(배열) 객체의 크기 = BaseSize + (# of elements * 4) // 32비트인 경우

확인을 위해 다음과 같은 간단한 class를 만들고,

public class CT
{
    int _i = 1;
    int _j = 2;
    int _k = 3;
}

공식을 확인할 수 있습니다.

ComponentSize = x64 포인터 크기 0x8 (x86 포인터 크기 0x4)

CT[0] = BaseSize 0x18 + 0 * 0x8 = 0x18
CT[1] = BaseSize 0x18 + 1 * 0x8 = 0x20
CT[2] = BaseSize 0x18 + 2 * 0x8 = 0x38




여기서 예외가 하나 있는 것이 바로 System.String입니다.

windbg - .NET string의 x86/x64 메모리 할당 구조
; https://www.sysnet.pe.kr/2/0/11336

System.String은 배열은 아니지만 내부 메모리 구조는 char 배열과 유사합니다. 단, null 처리로 인한 2바이트 추가로 인해 다음과 같이 공식이 바뀝니다.

string 객체의 크기 = BaseSize + (# of elements * ComponentSize) + 2 (null)
                  = BaseSize + (# of elements * sizeof(char)) + 2 (null)

이 공식을 사용하면 크기를 다음과 같이 자동으로 구할 수 있습니다.

new string('a', 0) = 0x18 + 0 * 2 + 2 = 0x1a
new string('a', 1) = 0x18 + 1 * 2 + 2 = 0x1c
new string('a', 2) = 0x18 + 2 * 2 + 2 = 0x1e
new string('a', 3) = 0x18 + 3 * 2 + 2 = 0x20

정리해 보면, x86/x64에 따라 다음과 같이 일반화할 수 있습니다.

[x86]
BaseSize = 0xc (0n12)

값 형식: 객체의 크기 = 12 + (# of elements * sizeof(element))
참조 형식: 객체의 크기 = 12 + (# of elements * 4)
System.String 크기 = 12 + (# of elements * 2) + 2

[x64]
BaseSize = 0x18 (0n24)

값 형식: 객체의 크기 = 24 + (# of elements * sizeof(element))
참조 형식: 객체의 크기 = 24 + (# of elements * 8)
System.String 크기 = 24 + (# of elements * 2) + 2




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







[최초 등록일: ]
[최종 수정일: 7/7/2021]

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)
10900정성태2/19/201622174.NET Framework: 548. Linq는 결국 메서드 호출! [3]파일 다운로드1
10899정성태2/17/201623463개발 환경 구성: 282. kernel32.dll, kernel32legacy.dll, api-ms-win-core-sysinfo-l1-2-0.dll [1]
10898정성태2/17/201621999.NET Framework: 547. PerformanceCounter의 InstanceName 지정 시 주의 사항파일 다운로드1
10897정성태2/17/201621283디버깅 기술: 76. windbg 분석 사례 - 닷넷 프로파일러의 GC 콜백 부하
10896정성태2/17/201622387오류 유형: 320. FATAL: 28000: no pg_hba.conf entry for host "fe80::1970:8120:695:a41e%12"
10895정성태2/17/201621266.NET Framework: 546. System.AppDomain으로부터 .NET Profiler의 AppDomainID 구하는 방법 [1]
10894정성태2/17/201621972오류 유형: 319. Visual Studio에서 찾기는 성공하지만 해당 소스 코드 정보가 보이지 않는 경우
10893정성태2/16/201620625.NET Framework: 545. 닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까? - 두 번째 이야기
10892정성태2/16/201621223오류 유형: 318. 탐색기에서 폴더 생성/삭제 시 몇 초 동안 멈추는 현상
10891정성태2/16/201624272VC++: 95. 내 CPU가 MPX/SGX를 지원할까요? [1]
10890정성태2/15/201624087.NET Framework: 544. C# 5의 Caller Info를 .NET 4.5 미만의 응용 프로그램에 적용하는 방법 [5]
10889정성태2/14/201620432.NET Framework: 543. C++의 inline asm 사용을 .NET으로 포팅하는 방법 - 두 번째 이야기파일 다운로드1
10888정성태2/14/201618733.NET Framework: 542. 닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까?
10887정성태2/3/201619439VC++: 94. MPX(Memory Protection Extensions) 테스트파일 다운로드1
10886정성태2/3/201620682개발 환경 구성: 281. Intel MPX Runtime Driver 수동 설치
10885정성태2/2/201620368오류 유형: 317. Sybase.Data.AseClient.AseException: The command has timed out.
10884정성태1/11/201621609개발 환경 구성: 280. 닷넷에서 SAP Adaptive Server Enterprise 데이터베이스 사용파일 다운로드1
10882정성태1/6/201620893Windows: 113. 윈도우의 2179, 26143, 47001 TCP 포트 사용 [1]
10881정성태1/3/201622310오류 유형: 316. 윈도우 10 - 바탕/돋음 체가 사라져 한글이 깨지는 현상 [2]
10880정성태12/16/201520020오류 유형: 315. 닷넷 프로파일러의 오류 코드 정보
10879정성태12/16/201521962오류 유형: 314. Error : DEP0700 : Registration of the app failed. error 0x80070005
10878정성태12/9/201525101디버깅 기술: 75. UWP(유니버설 윈도우 플랫폼) 앱에서 global::System.Diagnostics.Debugger.Break 예외 발생 시 대응 방법
10877정성태12/9/201529447VC++: 93. std::thread 사용 시 R6010 오류 [2]
10876정성태11/26/201525516.NET Framework: 541. SignedXml을 이용한 ds:Signature만드는 방법 [3]파일 다운로드1
10875정성태11/26/201530508개발 환경 구성: 279. signtool.exe의 다중 서명 기능 [2]
10874정성태11/26/201526476개발 환경 구성: 278. 인증서와 인증서를 이용한 코드 사인의 해시 구분
... [121]  122  123  124  125  126  127  128  129  130  131  132  133  134  135  ...