Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

windbg - SOS DumpClass/DumpMT의 "Vtable Slots", "Total Method Slots", "Slots in VTable" 값에 대한 의미

windbg에서 System.String 타입에 대해 DumpClass 명령을 수행해 보겠습니다.

0:000> !DumpClass 00007ff961df50e0
Class Name:      System.String
mdToken:         0000000002000073
File:            C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Parent Class:    00007ff961df5188
Module:          00007ff961df1000
Method Table:    00007ff9624f6948
Vtable Slots:    1b
Total Method Slots:  1d
Class Attributes:    102101  
Transparency:        Transparent
NumInstanceFields:   2
NumStaticFields:     1
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9624f9288  400026f        8         System.Int32  1 instance           m_stringLength
00007ff9624f7b00  4000270        c          System.Char  1 instance           m_firstChar
00007ff9624f6948  4000274       90        System.String  0   shared           static Empty
                                 >> Domain:Value  000002c18d980b20:NotInit  <<

위와 같이 "Vtable Slots"와 "Total Method Slots" 값이 나옵니다. "Vtable"이라는 단어로 봐서 분명 가상 메서드와 관련이 있을 것 같은데,

C++의 가상 함수 테이블 (vtable)은 언제 생성될까요?
; https://www.sysnet.pe.kr/2/0/11167

C++ 클래스 상속 관계의 vtable 생성 과정
; https://www.sysnet.pe.kr/2/0/11168

2개로 나누어져 있는 이유를 모르겠습니다. 게다가 DumpMT로 System.String 타입의 메서드 테이블을 살펴보면,

0:000> !DumpMT 00007ff9624f6948
EEClass:         00007ff961df50e0
Module:          00007ff961df1000
Name:            System.String
mdToken:         0000000002000073
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: 194
Number of IFaces in IFaceMap: 7

"Slots in VTable"이란 값도 나옵니다. 이렇게 3가지로 나누어져 있다 보니 더욱 혼란스럽습니다. ^^;

이 중에서 "Slots in VTable"은 "!DumpMT -md <addr>" 명령을 내리면 그 의미를 확실히 알 수 있습니다. 왜냐하면 실제로 194개의 메서드 목록이 나오기 때문입니다. 그럼 한 가지는 정리되었군요. ^^ "Slots in VTable" 값이야말로 해당 타입이 (상속을 포함해) 소유하고 있는 전체 메서드의 수를 가리킵니다.

남은 것은 "Vtable Slots"와 "Total Method Slots"의 의미인데요. 이게 좀 재미있습니다. ^^




참고로 검색해 보면 다음과 같은 질문 글이 나옵니다.

SOS and DumpClass
; https://social.msdn.microsoft.com/Forums/vstudio/en-US/13839da8-bc06-4443-9a1b-5e326e386ff8/sos-and-dumpclass?forum=clr

정리하면, 너무 난이도가 있는 질문이라 더 진행하려면 "유료" 기술 지원을 신청해야 한다는 답변이 나옵니다. 그래서 저도 그냥 접을까 하다가 호기심이 생겨 ^^; 좀 더 살펴봤습니다.

우선 예를 들기 위해 다음의 코드로 시작해 보겠습니다.

using System;

namespace ConsoleApp2
{
    class ABC
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Dump(new ABC());
        }

        private static void Dump(ABC aBC)
        {
            Console.ReadLine();
        }
    }
}

아무것도 정의하지 않은 ABC 클래스이지만 DumpClass로 살펴보면,

0:000> !DumpClass 00cf16ec
Class Name:      ConsoleApp2.ABC
mdToken:         02000002
File:            F:\cloud_drive\Dropbox\articles\compoentSize\ConsoleApp2\ConsoleApp2\bin\Debug\ConsoleApp2.exe
Parent Class:    715445e0
Module:          00cf3ffc
Method Table:    00cf4d88
Vtable Slots:    4
Total Method Slots:  5
Class Attributes:    100000  
Transparency:        Critical
NumInstanceFields:   0
NumStaticFields:     0

"Vtable Slots == 4", "Total Method Slots == 5"가 나옵니다.

우선 결론부터 설명하면 "Vtable Slots"은 가상 함수의 수를 의미합니다. 그런데 어떻게 ABC 클래스에 4개가 있을까요? Visual Studio에서 object 타입에 대해 F12 키를 눌러 메타데이터 정의로 들어가 보면 다음과 같이 3개의 가상 함수를 확인할 수 있습니다.

using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;

namespace System
{
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    public class Object
    {
        [NonVersionableAttribute]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public Object();

        [NonVersionableAttribute]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        ~Object();

        [NonVersionableAttribute]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        public static bool ReferenceEquals(Object objA, Object objB);
        public virtual bool Equals(Object obj);
        public virtual int GetHashCode();
        [SecuritySafeCritical]
        public Type GetType();
        public virtual string ToString();
        [SecuritySafeCritical]
        protected Object MemberwiseClone();
    }
}

아니, 그런데 분명히 "Vtable Slots"의 값은 4로 나오는데 어째서 3일까요? 왜냐하면 ~Object로 표현된 소멸자가 실은 다음과 같이 가상 함수이기 때문입니다.

// .NET Reflector 등을 통해 보면 virtual 메서드로 나옴

protected virtual void Finalize();

따라서 windbg의 출력 결과인 4가 맞습니다. 그렇다면 "Vtable Slots"이 가상 함수의 수라는 것은 알겠는데 뜬금없이 "Total Method Slots == 5"가 나오는 것은 왜일까요?

그것은 기본 생성자(ctor)와 기본 정적 생성자(cctor)가 "Total Method Slots"의 수에 포함되기 때문입니다. ABC 클래스의 경우 기본 생성자가 정의되어 있기 때문에 5가 나오는 것입니다. 그런데 재미있는 것은 일반 생성자를 하나 정의해서 기본 생성자의 자동 추가를 막게 되면,

class ABC
{
    public ABC(char ch) { }
}

이번에는 ctor가 없어졌으므로 다음과 같은 상태로 바뀝니다.

Vtable Slots = 4
Total Method Slots = 4

물론 다시 기본 생성자를 추가하면 Total Method Slots는 5로 바뀝니다.

class ABC
{
    public ABC(char ch) { }
    public ABC() { }
}

/*
Vtable Slots = 4
Total Method Slots = 5
*/

즉, 다른 생성자들은 전혀 상관이 없고 오직 기본 생성자에 한해서만 Total Method Slots의 수가 바뀌는 것입니다.

마찬가지로 정적 생성자도 Total Method Slots의 수에 반영됩니다.

class ABC
{
    public ABC(char ch) { }
    public ABC() { }

    // 또는 정적 생성자가 생성되도록 유발하는 정적 필드 초기화가 있는 경우에도!
    static ABC()
    {
    }
}
/*
Vtable Slots = 4
Total Method Slots = 6
*/




자, 그럼 이 지식들을 바탕으로 System.String을 다시 볼까요? ^^

정리하면 System.String은 다음과 같은 수치를 보입니다.

Vtable Slots:    1b (0n27)
Total Method Slots:  1d (0n29)
Slots in VTable: 194

그럼 수치들을 한번 맞춰보겠습니다. 우선 string은 다음과 같은 가상 메서드들을 구현하고 있습니다.

System.Object로부터 4개
IComparable로부터 1개
ICloneable로부터 1개
IConvertible로부터 17개
IEnumerable로부터 1개
IComparable<String>로부터 1개
IEnumerable<char>로부터 1개
IEquatable<String>로부터 1개
====
총 27개

그렇다면 "Vtable slots = 0x1b(0n27)"은 만족합니다. 그런데 Total Method Slots의 수가 29가 나오다니... 이건 좀 이상하군요. 분명히 System.String에는 기본 생성자도, 정적 생성자도 없기 때문에 Total Method Slots의 수도 27이 나와야 맞습니다.

당연하겠지만 여기서 고려하지 않은 조건이 있습니다. 바로 '제네릭' 메서드가 "Total Method Slots"에 반영된다는 점입니다. System.String을 보면 이런 메서드로 2개가 나옵니다.

public static String Join<T>(String separator, IEnumerable<T> values);
public static String Concat<T>(IEnumerable<T> values);

따라서 이것까지 고려해 Total Method Slots = 29가 나오는 것입니다.

확인을 위해 ABC 클래스를 이용해 다음과 같이 테스트해 볼 수 있습니다.

class ABC
{
    public ABC(char ch) { }
    public ABC() { }

    static ABC()
    {
    }

    void Test<T>(List<T> arg) { }
    void Test2<T>(List<T> arg) { }
}

/*
Vtable Slots = 4
Total Method Slots = 8 (Vtable Slots 4 + default ctor 1 + cctor 1 + generic method 2)
*/

정리가 되고 나니... 저도 마음이 편하군요. ^^




참고로, !DumpMT -md 명령어는 Vtable Slots, Total Method Slots, Slots in VTable에 속하는 메서드의 종류별로 묶어서 출력을 해줍니다. 가령 ABC 클래스를 다음과 같이 정의해 주면,

class ABC
{
    public ABC(char ch) { }
    public ABC() { }

    static ABC()
    {
    }

    void Test<T>(List<T> arg) { }
    void Test2<T>(List<T> arg) { }

    void MyMethod() { }
    static void StaticMyMethod() { }
}

!DumpMT -md의 출력은 다음과 같이 분류가 되어 출력됩니다.

the_number_of_method_1.png




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

[연관 글]






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

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

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12625정성태5/3/20219203오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/2/202112963.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [10]파일 다운로드1
12623정성태5/2/20219608.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용 [1]파일 다운로드1
12622정성태5/2/20216393오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/20219968.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법 [1]파일 다운로드1
12620정성태4/29/202111973.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202112467.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
12618정성태4/27/202111025사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신 [1]파일 다운로드1
12617정성태4/26/20218699.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/20218680.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/20216864오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/20219527개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core/5+ 웹 애플리케이션 예제 구성 [4]파일 다운로드1
12613정성태4/23/20218583.NET Framework: 1048. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드파일 다운로드1
12612정성태4/23/20218699.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/20217977오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/20217790.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/20219071.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/21/202110110.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [9]파일 다운로드1
12607정성태4/21/20218623.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/202112558.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/20218583.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/18/20217344VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
12603정성태4/16/20218216VS.NET IDE: 162. 비주얼 스튜디오 - 상속받은 컨트롤이 디자인 창에서 지원되지 않는 문제
12602정성태4/16/20219420VS.NET IDE: 161. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 [1]
12601정성태4/15/20218512.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
12600정성태4/15/20218717.NET Framework: 1039. C# - Kubeconfig의 token 설정 및 인증서 구성을 자동화하는 프로그램
... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...