← SEARCHABLE ARCHIVE
C# · 2026-08-10

C# 기초부터 마스터까지 — 9화

GC, 값 형식, 할당 비용으로 성능 이해하기

INTERACTIVE READING MODE →

본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.

C#은 GC가 메모리를 관리하지만 할당 비용이 사라지는 것은 아니다. 특히 게임처럼 일정한 프레임 시간이 중요한 프로그램에서는 언제 객체가 만들어지는지 알아야 한다.

값 형식과 참조 형식

public readonly struct Damage
{
    public int Value { get; }
    public Damage(int value) => Value = value;
}

Damage first = new(10);
Damage second = first;

값 형식은 변수에 값 자체가 들어가고 대입할 때 복사된다. 참조 형식은 관리 힙에 있는 객체를 가리키는 참조가 복사된다.

값 형식이 항상 스택에 있고 참조 형식이 항상 느리다고 외우면 안 된다. 실제 저장 위치와 비용은 사용 문맥, 크기, 런타임 최적화에 따라 달라진다.

boxing 피하기

int score = 100;
object boxed = score;
int restored = (int)boxed;

값 형식을 object나 구현한 인터페이스로 다루면 boxing이 발생할 수 있다. 제네릭 컬렉션을 사용하면 값 타입을 object로 바꾸지 않고 저장할 수 있다.

var scores = new List<int>();
scores.Add(100);

반복 할당 줄이기

private readonly List<Monster> visibleMonsters = new(128);

void CollectVisible()
{
    visibleMonsters.Clear();

    foreach (Monster monster in monsters)
    {
        if (monster.IsVisible)
        {
            visibleMonsters.Add(monster);
        }
    }
}

목록을 매번 새로 만들지 않고 용량을 가진 버퍼를 재사용하면 할당 횟수를 줄일 수 있다. 문자열 연결, 캡처 람다, LINQ 결과 생성도 반복 경로에서 할당 원인이 될 수 있다.

먼저 측정하기

GC 세대와 수집 방식은 런타임이 관리하며, 짧게 사는 객체가 무조건 문제인 것은 아니다. 가독성을 희생해 모든 코드를 미리 최적화하면 유지보수 비용만 커질 수 있다.

프로파일러로 할당량과 멈춤이 실제 병목인지 확인한 뒤 풀링, 버퍼 재사용, 값 타입을 적용해야 한다. 성능 최적화는 추측으로 문법을 바꾸는 작업이 아니라 측정된 비용의 원인을 줄이는 작업이다.