C# - 메모리 구조, GC

2025. 10. 13. 17:35·C#

Memory 구조

  • 프로그램 실행 시 메모리는 4개의 주요 영역으로 나뉨
  • 코드 (Code Segment) : 실행할 프로그램 코드 저장
  • 데이터 (Data Segment) : static 변수 저장
  • 스택 (Stack) : 함수 호출 시 생성되는 프레임, 지역변수 / 매개변수 / 반환 주소 저장
  • 힙 (Heap) : 동적으로 할당된 객체 저장

C# 관점

  • 스택 : 함수 호출과 로컬 변수 관리
  • 힙 : 동적 객체 저장, 참조형 타입 대부분 저장

 

 

Stack

함수 호출 시 생성되는 메모리 프레임 관리

  • 저장 내용
    • 매개변수
    • 지역변수
    • 반환주소
  • 특징
    • LIFO 구조 (Last In First Out) 후입 선출
    • 자동 할당/해제 → 빠름
    • 함수 종료 시 프레임 제거
void Foo()
{
	int x = 10; // x는 스택에 저장
	iny y = 20;
}

 

 

 

class Person
{
	public string Name;
}

class Program
{
	void Bar()
	{
		Person p = new Person(); // p는 스택에 참조(주소) 저장
		p.Name = "Kim"; // 실제 객체는 힙에 저장
	}
	
	void PrintSum(int x, int y)
	{
		int sum = x + y; // sum도 스택에 저장
		Console.WriteLine(sum);
	}
	
	struct Point
	{
		public int X, Y;		
	}
	
	void Draw()
	{
		Point pt = new Point { X = 10, Y = 20 }; // pt 구조체 전체가 스택에 저장
	}
}

 

 

Heap

동적 메모리 저장 공간

  • 저장 대상
    • new 키워드로 생성된 객체
    • 클래스 인스턴스, 배열, 문자열 등 참조형 타입 대부분
  • 특징
    • 동적 할당 → 런타임에 크기 결정 가능
    • 수명 → 참조가 살아 있는 동안 유지
    • 속도 → 스택보다 느림 (할당 접근 비용이 더 높음)
    • 관리 → GC가 필요할 때 수거
class Person
{
	public string Name;
}

class Program
{
	void Foo()
	{
		Person p = new Person(); // p는 스택 참조, Person 객체는 힙에 저장
		p.Name = "Kim";
	}
}

 

 

 

class Person
{
	public string Name;
	public int Age;
}

class ExampleClass
{
	public Person Friend; // 참조형 필드
	public int[] Scores; // 배열도 힙에 저장
}

class Program
{
	void HeapExamples()
	{
		Person p1 = new Person();
		p1.Name = "Kim";
		p1.Age = 25;
		
		Person p2 = new Person (Name = "Lee", Age = 30);
		
		// 참조 복사 (참조만 스택에, 객체는 힙)
		Person p3 = p2;
		
		// 클래스 내부에 참조형 필드
		ExampleClass example = new ExampleClass();
		example.Friend = new Person { Name = "Park", Age = 28 );
		
		example.Scroes = new int[] { 90, 85, 100 } // 배열도 힙에 저장
		
		ExampleClass example2 = example; // example2 참조는 스택, 객체는 힙
	}
}

 

 

Data Segment

readonly, static 변수 등 저장

프로그램 시작 시 생성, 종료 시 해제

모든 스레드에서 공유 가능

class Example
{
	public static int Count = 0;
	public static string Name;
}

 

 

Code Segment

프로그램의 실행 가능한 기계어 코드 저장, 실행해야할 명령어만 저장

읽기 전용 → 코드가 의도치 않게 변경되는 것을 방지

프로그램 시작 시 메모리에 로드, 종료시 해제

모든 스레드에서 공유 가능

class Program
{
	static void Main()
	{
		Foo(); // 코드 영역
	}
	
	void Foo()
	{
		Console.WriteLine("Hello, World!"); // 코드 영역에 위치
	}
}

 

 

 

GC - Garbage Collector

힙에 있는 더이상 참조되지 않는 객체를 자동으로 수거하여 메모리 해제

목적 : 메모리 누수 방지, 자동 메모리 관리

  • 대상
    • 힙에 있는 참조형 객체
    • 더 이상 스택이나 다른 객체에서 참조되지 않는 객체
class Person
{
	public string Name;
}

class Program
{
	void Foo()
	{
		Person p = new Person(); // 힙에 객체 생성
	} // p 참조 사라짐 -> GC 대상
}

 

  • 동작 원리
    • 객체 참조 추적
      • 스택 데이터 영역, 다른 객체에서 참조되는 객체는 살아있음
      • 참조가 없는 객체는 수거 대상
    • 세대 관리
      • Gen 0 : 새로 생성된 객체
      • Gen 1 : Gen 0 에서 살아남은 객체
      • Gen 2 : 오래 살아남은 객체
      • 목적 : 짧은 수명 객체를 빠르게 수거, 오래된 객체는 덜 검사
    • GC 과정
      • 마킹 (Mark) : 살아있는 객체 표시
      • 스윕 (Sweep) : 참조 없는 객체 제거
      • 압축 (Compact) : 단편화 제거, 힙 연속성 유지

스윕 전에 마킹을 수행하는 이유 예시

class Node
{
	public string Name;
	public Node Next;
}

class Program
{
	void Example()
	{
		// B 객체 생성
		Node b = new Node { Name = "B" };
		
		// A 객체 생성, B 객체를 Next로 참조
		Node a = new Node { Name = "A", Next = b };
		
		// 외부에서 a만 참조하고 b는 외부 참조 없음
		Node root = a;
		
		// 이제 a만 남음, b는 a 내부에서만 참조
		// GC 시작
	}
}

 

 

 

객체 참조 추적 (root Set 탐색)

  • 목적 : 어떤 객체가 살아있는지 확인
  • 루트(Root) 객체 확인
    • 스택 : 지역 변수, 매개변수
    • 데이터 영역 : static 변수, 전역 변수
    • 레지스터 : CPU에 임시 저장된 참조
  • 루트에서 접근 가능한 모든 객체는 살아있는 객체로 표시
static Person p; // 데이터 영역

void Foo()
{
	Person local = new Person(); // 스택 참조
}

 

p와 local에서 참조되는 객체는 살아있음

스택 프레임 종료 후 local은 더 이상 루트에 없으므로 수거 대상

 

 

 

세대(Generation) 관리

  • 목적 : 효율적인 수거
  • 방식 : GC 수거에서 살아남은 횟수를 기반으로 세대 결정
  • 효과 : 짧은 수명 객체를 자주, 오래된 객체는 덜 검사 → 성능 최적화
세대 대상 특징
Gen 0 새로 생성된 객체 대부분 짧은 수명 → 자주 수거
Gen 1 Gen 0 에서 살아남은 객체 중간 수명, 적당히 수거
Gen 2 오래 살아남은 객체 장기 수명, 수거 빈도 낮음

 

 

 

GC 과정 (Mark-Sweep-Compact)

  • 마킹 (Mark)
    • 루트에서 도달 가능한 객체 모두 표시
    • 살아있는 객체 = Marked
    • 루트에서 접근 불가하면 Garbage 후보
  • 스윕 (sweep)
    • 힙 전체를 순회하며
      • Marked → 유지
      • Mark되지 않은 → 삭제, 메모리 해제
    • 단순 Mark-Sweep은 메모리 단편화 발생 가능
  • 암축 (Compact)
    • 삭제 후 남은 객체를 연속된 공간으로 이동
    • 목적 : 단편화 제거, 힙 연속성 유지
    • 스택 / 힙 접근시 캐시 효율 증가

GC 시점

  • 자동 : 힙 사용량, 메모리 부족, 주기적 검사
  • 수동 호출 : Gc.Collect() → 즉시 수거 (권장 안 함, 성능 영향)

 

class MyClass
{
	public int Id;
	public MyClass(int id) { Id = id; }
    
	~MyClass()
    {
		Console.WriteLine($"MyClass {Id} 소멸자 호출!");
    }
}
static void Main()
{
    Console.WriteLine("프로그램 시작");

    //객체 생성 (힙에 할당)
    MyClass obj = new MyClass(1);

    // WeakReference 생성 (객체 생존 여부 확인용)
    WeekReference weakRef = new WeakReference(obj);

    Console.WriteLine("객체 생성 후 alive? " + weakRef.IsAlive); // True

    // 아직 참조가 살아있는 상태에서 GC 시도
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("GC 수행 후 alive? " + weakRef.IsAlive);// True

    // 참조 제거
    obj = null;

    // obj 자체는 null이지만, 객체가 아직 살아있는지 확인
    Console.WriteLine("obj 참조 제거 직후 obj != null? " + (obj != null)); // False
    Console.WriteLine("obj 참조 제거 직후 WeakRef.IsAlive? " + weakRef.IsAlive); // True

    // GC 수행
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("GC 수행 후 alive? " + weakRef.IsAlive); // False

    Console.WriteLine("프로그램 종료");
}

 

'C#' 카테고리의 다른 글

C# - LockConsoleSize() : 콘솔 창 크기 고정 및 리사이즈 방지  (1) 2025.10.14
C# - 콘솔 폰트 크기 변경하기 SetCurrentConsoleFontEx  (1) 2025.10.13
C# - 정렬 알고리즘 (Sorting Algoritm) : 버블/선택/삽입/병합/퀵 정렬  (0) 2025.10.13
C# - 피셔 예이츠 셔플 (Fisher Yates Shuffle)  (0) 2025.10.13
C# - 가중치 그래프 - 다익스트라 알고리즘 으로 지하철 노선 최단경로 구하기  (0) 2025.10.13
'C#' 카테고리의 다른 글
  • C# - LockConsoleSize() : 콘솔 창 크기 고정 및 리사이즈 방지
  • C# - 콘솔 폰트 크기 변경하기 SetCurrentConsoleFontEx
  • C# - 정렬 알고리즘 (Sorting Algoritm) : 버블/선택/삽입/병합/퀵 정렬
  • C# - 피셔 예이츠 셔플 (Fisher Yates Shuffle)
ellug
ellug
Web / App / Game Dev
  • ellug
    dev.ellug
    ellug
  • 전체
    오늘
    어제
    • 분류 전체보기 (95)
      • Javascript (React, Next) (1)
      • C# (66)
      • Graphics (1)
      • Unity (25)
      • 프로그래머스 (0)
        • Lv.0 (0)
        • Lv.1 (0)
        • Lv.2 (0)
        • Lv.3 (0)
      • AI Agent (0)
      • StableDiffusion (0)
      • ETC (1)
      • 잡담 (0)
      • 유머 (0)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    useState
    최적화
    Design Pattern
    추상 클래스
    큐
    자료구조
    스택
    list
    stack
    C#
    클래스
    Photon
    네트워크
    Abstract
    알고리즘
    interface
    class
    Network
    firebase
    게임
    function
    Queue
    inputsystem
    delegate
    콘솔
    터미널
    console
    unity
    struct
    유니티
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
ellug
C# - 메모리 구조, GC
상단으로

티스토리툴바