Visual Studio 2022

 

class 생성

멤버변수 int 하나

public class Test
{
	int data_1;
}

public static void Main(string[] args)
{
	Test test = new Test();
}

C# Class Size Test (Field int 1)

int 하나인데 크기가 24 바이트이다.

int = 4byte 인데 왜 6배나 되는걸까?

=>6개 이상일 때 크기 확인 해보기로함

public class Test
{
	public int data_1;
	public int data_2;
	public int data_3;
	public int data_4;
	public int data_5;
	public int data_6;
}

일단 int 6개로 테스트, 기대 결과는 그대로 24byte

C# Class Size Test (Field int 6)

이게 무슨일이지, 40 byte 가 나옴

처음 결과를 기준으로 생각해보면 24 + 16 즉 16 byre가 추가됨.

4(int) * 4(count) 라고 생각해보고 다시 필드를 int * 3으로 테스트

 

public class Test
{
    public int data_1;
    public int data_2;
    //public int data_3;
    //public int data_4;
    //public int data_5;
    //public int data_6;
}

예상한대로면 처음 결과인 24가 나와야함

C# Class Size Test (Field int 2)

결과는 기대한대로 나왔지만 여전히 24라는 크기가 이해가 안감,

혹시나해서 클래스를 인스턴스화 안했을때 한번 확인해봤지만 당연하게 해당 메모리 안잡힘

 

이번엔 하나씩 필드 늘려가면서 확인해보기로함

C# Class Size Test (Field int 3)

int * 4 일때는 동일하게 32

C# Class Size Test (Field int 5)

int * 5 부터 40 으로 잡혔음

 

그럼 그냥 빈 클래스는 얼마로 잡히나 확인했는데

애초에 비어있는 클래스를 인스턴스하면 24 byte 할당됨

여기서 2개씩 int 추가될 때마다 8 byte가 늘어나는데 이 늘어나는 값은 4(int) * 2(count) 맞음

 

비어있는 클래스가 왜 24 byte의 힙을 차지하는지 파악해보면될듯함

일단 Program 외부에 Test_2 클래스, 내부에는 Test_1 선언 후 비교

public class Test_2 { }
public class Program
{
    public class Test_1 { }
    public static void Main(string[] args)
    {
        Test_1 test_1 = new Test_1();
        Test_2 test_2 = new Test_2();
    }
}

C# Class Size Test_1 / Test_2 (empty)

테스트 해본 결과 클래스를 할당하면 기본 크기가 24로 잡히고 이 안에 8byte 까지는 포함시킬 수 있는걸로 보인다.

즉 기본 24 = 16(사용됨) + 8(여유 공간) 이고 크기가 24 byte를 넘을 경우 8byte 단위로 확장되는걸로 파악됨

 

구글링 해본 결과 내가 원하는 정보를 찾을 수 있었다.

 

OF MEMORY AND STRING_Jon Sket

 

본래 글의 취지는 .Net에서 ASCII 문자열의 효율적인 표현에 관한것에 대한 답이다.

위 글에서는 x86과 x64를 모두 비교하고 있는데 이 글에서는 x64를 기준으로 정리한다.

 

그럼 이제 왜 0byte는 될수없는지 그리고 24byte 중 사용할 수 없는 16byte와 사용 가능한 8byte는 무엇 때문인지 정리한다.

 

우선 C#에서 모든 타입은 object 타입을 베이스로한다. 그리고 이 object 타입의 크기는 24byte이다.

이 24byte의 크기는 16byte의 오버헤드와 참조를 위한 8byte로 이루어진다. 

 

오버헤드란 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리 등을 말한다. 즉 C#의 object를 메모리에 유지시키는데 필요한 메모리 양이 16byte이며 .Net의 CLR은 데이터가 없는 object에서 작업할 수 없기 때문에 오버헤드만 가지고는 object를 사용할 수 없고 여유 공간이 필요하며 이 크기가 8byte이다. 따라서 선언될 수 있는 가장 작은 크기의 메모리는 24byte가 된다.

 

이 중 여유공간인 8byte는 실제 데이터를 저장할 수 있기 때문에 int가 2개 포함된 class역시 24byte였다. 이 8byte는 전체를 사용할 수 있는 공간이기 때문에 1byte로 8를 사용하는것도 가능하다. 그리고 int가 3개가 될때 24byte에서 8byte로 증가했는데 이것은 메모리의 경계인 패딩의 크기가 8byte였기 때문이다. 

 

class 역시 object 타입을 베이스로 하고 있고 빈 class를 인스턴스화 하는것은 object를 생성하는것과 마찬가지이기 때문에 빈 class가 24byte라는것도 이제 납득이 간다. 

 

오버헤드에 대한 내용

overhead - Andrew Hunter
overhead - Stack Overflow

 

왜 이렇게 많은 공간이 필요한지 또 이 공간은 어떤식으로 사용되는지 궁금한데 이 부분은 더 자세히 찾아봐야 될것같다.

 

정리

64bit 기준으로 

1. 비어있는 class 또한 하나의 object이다. 따라서 24byte 메모리를 차지한다.

2. 이 메모리는 16byte의 내부 데이터 참조형으로 사용되는 오버헤드와 8byte의 여유공간으로 구성된다.

3. 여유공간은 모두 사용이 가능하며 메모리는 8byte의 패딩을 가지고 있다.

 

728x90
반응형

'Memo' 카테고리의 다른 글

다국어를 제공하는 방법에 대해서  (0) 2023.05.10

Interoperability(Interop)

상호운용성

unmanaged code에 대한 기존 투자를 보존하고 활용할 수 있게 한다.

즉 CLR을 사용하지 않는 어셈블리를 CLR에서 사용할 수 있게 만드는 것이다.

interop은 managed와 unmanaged를 오고가는 메모리 비용과 코드 작성 비용 때문에 최소화하는게 좋다.

 

COM Interop

Component Object Model(COM) 을 사용하면 개체의 기능을 다른 컴포넌트와 윈도우 플랫폼의 호스트 프로그램에 사용할 수 있다. 사용자가 기존 코드 베이스와 상호 운용할 수 있도록 .Net 프레임워크에서 COM라이브러리를 통해 interop을 지원한다.

 

Platform Invoke(P/Invoke)

P/Invoke는 사용자의 managed code에서 unmanaged 라이브러리의 구조, 콜백 그리고 기능에 접근할 수 있도록 한다. 대부분 P/Invoke API는 SystemSystem.Runtime.InteropServices 두 개의 네임스페이스를 포함한다.

이 두개의 네임스페이스를 사용하면 네이티브 컴포넌트를 사용할 수 있는 도구가 제공된다.

using System;
using System.Runtime.InteropServices;

public class Program
{
	// Import dll (containing the funtion)
    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)
    // Define
    private static extern int MessageBox(IntPtr hWnd, string IpText, string IpCaption, uint uType);
    
    public static void Main(string[] args)
    {
    	// Invoke the function as a regular managed method
        MessageBox(IntPtr.Zero, "Command-line message box", "Attention!", 0);
    }
}
  1. Attribute
    • [DllImport] attribute은 런타임에 unmanaged DLL을 로드할것을 알린다.
    • "user32.dll" 문자열은 사용할 기능이 포함된 DLL을 대상으로 한다.
    • CharSet = CharSet.Unicode 문자열을 Mashalling하는데 사용할 문자 집합을 지정한다.
    • SetLastError = true 런타임에서 오류가 발생했을때 사용자가 Mashal.GetLastWin32Error()를 통해서 에러코드를 감지할 수 있게한다.
  2. Declare
    • 메서드 선언시 메서드명은 사용할 unmanaged code와 동일한 이름으로 작성한다.
    • 이때 extern 키워드를 통해서 해당 메서드가 외부의 메서드임을 런타임에 알린다.
    • 런타임은 외부 메서드라는것을 알게되면 해당 managed 메서드를 호출할 때 attribute의 설정을 통해 특정된 DLL을 찾아서 실행시킨다.
  3. Main method
    • managed code 즉 현재 작업중인 코드에서 extern으로 선언한 메서드를 호출하여 외부 메서드를 사용한다. 

 

Managed C++(C++/CLI)

Managed Extensions for C++ 또는 Managed C++은 문법적 그리고 구문적 확장, 키워드와 속성을 포함하고 C++의 구문 및 언어를 가져오는 .Net Framework의 C++ 언어 확장 집합이다. 이 확장자는 managed code상에서 C++ 코드가 CLR의 대상이 될 수 있도록 그리고 지속적으로 native code와 상호 운용될 수 있게하려고 마이크로소프트에서 만들었다.

 

2004년 Managed C++은 구문을 명확하고 단순화하고 managed generics을 포함하도록 기능을 크게 개선시켰다. 이렇게 새로운 확장자는 C++/CLI로 지정되었고 Visual Studio 2005 이후에 포함되면서 Managed C++을 대체하였다.

 

SWIG

Simplefied Wrapper and Interface Generator

C 또는 C++로 작성된 컴퓨터 프로그램이나 라이브러리 다른 프로그래밍 언어에서 사용할 수 있도록 연결하는데 사용하는 오픈소스 도구이다. 

주목적은 네이티브 함수의 호출, 복잡한 자료형을 함수에 전달, 메모리 부적절하게 해제하지 못하게 방지, 언어 간에 오브젝트 클래스를 상속할 수 있게 하는것이다.

728x90
반응형

Managed code

.Net Framwork에서 CLR(Common Language Runtime)의 제어하에서 실행되는 코드를 말한다.

이 코드는 Visual Basic.Net이나 C#과 같은 .Net Framework를 지원하는 언어의 컴파일러를 통해서 만들어지는 코드로 컴파일러에 의해 IL(Intermediate Language)이라는 중간 언어로 생성된다. IL은 컴퓨터에서 바로 실행할 수 있는 기계 언어가 아니며 사용자가 생성한 코드의 클래스, 메서드, 속성 등을 나타내는 메타데이터와 함께 어셈블리라는 파일로 저장된다.

 

CLR은 이러한 어셈블리를 실행할 때 코드의 보안, 메모리 관리, 스레딩같은 관리를 담당하는 다양한 서비스를 제공하며 실행 시점에 필요한 코드는 JIT 컴파일로 컴퓨터의 환경에 맞는 기계어로 변환되어 실행된다. 이런 특징 때문에 CLR은 Managed Program이라고도 불리며  Managed Code를 사용하기 때문에 이러한 동작이 가능하다.

 

unmanaged code

CLR 외부에서 실행되는 코드

unmanaged code는 Visaul Studio.Net 2002가 나오기 전에 만든 코드를 말한다. 즉 Visual Basic 6, Visual C++6 과 같은 컴파일러는 unmanaged code를 생성한다. 이 컴파일러들은 managed code처럼 IL 생성과정없이 컴파일이 수행되는 해당 컴퓨터에 적합한 기계 코드를 생성해 낸다. 이렇게 생성된 기계 언어는 동일한 구성의 다른 컴퓨터에서 실행될 수 있을지도 모르지만 다른 구성의 컴퓨터의 경우 실행이 불가능하다. 또한 이렇게 생성된 unmanaged code는 보안 및 메모리 관리 서비스를 실행시에 Runtime으로부터 받을 수가 없고 대신에 COM call의 서비스를 통해 받을 수 있다.

 

정리하자면 managed code의 경우 runtime이 알아서 메모리 관리를 해주는데 반해 unmanaged code의 경우, 사용자가 관리해야하지만 COM과 같은 라이브러리의 경우 스스로 능동적으로 메모리 관리할 수 있다.

 

native code

native code는 unmanaged code와 동일한 의미로 사용된다. 옛날 버전의 컴파일러로 컴파일 되었거나 선택적으로 해당 컴퓨터에 적합하게 생성된 기계 언어를 말하며 실행시 CLR에 의한 서비스를 받을 수 없는 것을 말한다. 이러한 native code는 하나의 완전한 프로그램이거나 COM 컴포넌트 또는 managed code에서 호출된 DLL일 수도 있다. 

 

또 다른 의미로는 JIT 컴파일로부터 생성된 코드를 말한다. JIT은 managed code에만 사용되며 이는 managed code이지만 IL코드가 아니며 해당 컴퓨터에 적합한 기계언어이다. 따라서 native가 무조건 unmanaged는 아니다.

 

 

 

728x90
반응형

'Program Language > C#' 카테고리의 다른 글

C# 인스턴스 생성 키워드 : new  (0) 2023.03.30
Interoperability  (0) 2023.02.02
C# 외부 코드 사용 키워드 : extern  (1) 2023.01.31
C# 키워드 : delegate, event, action  (0) 2023.01.31
C# 상수 키워드 : const  (0) 2023.01.31

+ Recent posts