한국어
Unity Roadshow 2026 배운 내용 정리

Unity Roadshow 2026 배운 내용 정리

Unity Roadshow 행사 가서 배운 내용들 간단 기록

메모리 사전 지식

기본 내용

  • 우리는 가상 메모리 (Virtual Memory, 이하 VM) 를 사용하며, 물리 메모리를 직접적으로 컨트롤하지 않는다

  • VM은 페이지로 구성되어 있으며, OS마다 상이할 수 있음

    • Window / Linux 4kb

    • IOS, MacOS, Android 16kb

  • 페이지에 대응하는 물리 메모리의 프레임 존재

  • 메모리 압박 수준에 따라서 가상 메모리를 물리 메모리에 병합한다

Page Fault

물리 메모리에 로드되지 않은 페이지가 필요한 경우를 Page Fault라고 함

  • Clean Page: 원본 데이터가 변하지 않은 Page, OS에서 언제든 해지할 수 있고 회수가 쉬움

  • Dirty Page: 응용 프로그램이 새로 작성하거나 수정한 정보를 가진 페이지, 메모리에서 바로 내릴 수 없다

Memory Footprint

실질적인 메모리 압박을 표현하는 지표이며, 전체 더티 페이지 크기를 합산한 결과

Memory Footprint != 물리 메모리 사용량, 상주 메모리 사용량

메모리 관리법 (Windows vs Linux)

  • 예약: 물리 메모리를 사용하지 않고 가상 메모리만 할당

  • 커밋: 실제 가상 메모리만큼의 물리 프레임을 할당

  • 디커밋: 예약 공간을 유지하면서 물리 프레임을 해제하는 것

Windows: 명시적인 API 제공 (virtual alloc, virtual free)

UNIX Like: 예약과 커밋이 명시적 분리가 없음

  • mmap 호출은 암시적으로 메모리를 예약한다

  • 페이지 바로 접근 시 지연 커밋이 발생

  • 명시적 API 없으며, 디커밋은 힌트로 동작한다

  • OS가 언제 페이지를 회수할지 결정함

Unity GC에서의 처리

Managed Heap

  • C# 메모리 할당은 전부 여기서 이루어짐

  • Garbage Collector 가 관리

  • Unity Profiler에서 GC.Alloc으로 표기

Garbage Collector

  • 주기적으로 유효한 참조가 존재하지 않는 Object를 Heap에서 수집

  • GC를 수행해도 가상 메모리 총 사용량은 줄어들지 않음

  • 수집한 오브젝트 공간도 OS 기준으로는 Dirty

Segment

연속된 페이지들의 모음을 Segment라 정의

  • Managed Heap은 Raw Page가 아닌 Segment 사용

  • 한 Segment에 여러 Object들이 할당될 수 있음

  • 이를 할당하고 푼 뒤, 내부적으로 미사용 중인 상황이 있지만 RAM 사용으로 표기 (Dirty이므로)

  • 대부분은 전혀 문제가 없고 곧 재사용된다

  • 너무 오래 비어있는 케이스는 상주 메모리를 낭비하는 것이기 때문에 간헐적으로 Unity GC가 처리

Unity GC가 하는 일

  • 리맵핑 전략을 사용함

  • GC가 다수 실행된 뒤, Old Empty Segment들에 대해 Drop 후 같은 크기의 New Segment를 할당

  • Windows Remap 전략: Segment 해제 없이 Decommit (명시적 처리가 되기 때문)

  • UNIX Like Remap 전략: munmap & mmap을 통한 Remap ((Hot Page 효과로 캐시 적중률이 높음)

  • 이를 통해서 상주 메모리를 줄일 수 있다는 장점이 있음

GC.Collect 여러 번 실행하면 안되는 이유

  • 세그먼트 릴리즈를 강제로 실행하지 말 것

  • Collect 비싸고, 앱 프리징될 수 있음

  • 의도 자체가 오랫동안 비어있는 세그먼트의 디커밋

  • 잠깐 빈 세그먼트 디커밋은 의도와 다름

  • 강제로 디커밋 시 성능 개선은 없고 CPU 비용만 발생

    • 어차피 다음 프레임에 재사용되는 세그먼트도 디커밋되는 상황이 유발

Managed Heap의 VM 사용량은 커지기만 하고 절대 줄지 않음

  • Heap은 커지기만 하고 줄어들지 않음

  • Boehm GC의 원래 특징

  • 가상 메모리 할당량을 Trim할 이유가 없음

    • 큰 가상 메모리 != 큰 물리 메모리 사용량

    • 세그먼트 어차피 재사용

    • 게임 오브젝트들 매 프레임 자주 생성 및 파괴됨

    • 오브젝트가 파괴될 때마다 세그먼트를 줄이는 것은 CPU 성능만 낭비됨

Unity Boehm GC

  • Unity의 Incremental Boehm GC는 보수적인 GC (Conservative GC)

  • 이는 유니티가 과거 작성한 C# 코드를 C++로 변환하는 작업을 지원하는 IL2CPP을 제공하면서주로 사용하게 되었음

    • IL2CPP을 통해 다양한 환경에 대한 지원을 가져갈 수 있게 되었으나, C#과 달리 메모리 GC가 자동화되지 않았기 때문에 오픈소스 GC인 Boehm GC를 사용했던 것으로 보임
  • 성능보다 안정성이 중심으로, 포인터 "처럼" 보이는 모든 값을 참조로 간주

  • GC 도중 오브젝트의 위치를 변경하지 않음 -> 컴팩팅이 없고, 메모리 파편화에 취약

Compaction에 대해

  • 메모리 파편화 해소에 유리한 메모리 압축 작업. 사용하지 않는 공간을 전부 비워내어 재정렬

  • 정확한 참조 정보가 필요하기 때문에 보수적인 GC에서는 불가능함

  • 오브젝트의 위치를 옮기는 데에는 비용이 발생 (Stop The World + 실제 이동)

CoreCLR GC

CoreCLR GC는 Precise GC로 메모리 스택 및 레지스터에 있는 값 중 어떤 것이 레퍼런스인지 정확하게 알고 있는 방식의 GC임

그리고 .NET 은 CoreCLR을 코어 엔진으로 사용하고 있음 (CLR: Common Language Runtime)

CoreCLR이 지원하는 점

  • CoreCLR GC

  • JIT 컴파일러

  • Excecption Control

  • misc...

이 CoreCLR GC (이하 닷넷 GC) 는 세대 기반 메모리 관리 방식을 사용함

  • Heap을 3개 세대로 나누어 관리

  • 0세대: 방금 생성된 새로운 객체 위주, 빈번하고 빠른 GC가 발생 (대부분 여기서 객체가 죽게 됨)

  • 1세대: 0세대에서 살아남은 객체들이 이동하는 Buffer 역할

  • 2세대: 오랫동안 살아남은 무거운 객체가 존재하는 곳, GC가 이곳을 정리하는 것은 비용 소모가 큰 편

  • LOH: 아주 큰 객체는 이 공간으로 배치

프로모션 과정에서 메모리 Compaction을 처리하게 되면서 단편화를 줄일 수 있음

LOH의 경우 STW가 길어지기 때문에 오래 전에는 Compaction 하지 않았지만 지금은 명시적으로 옵션화해서 처리하고 있다고 함

전환의 가치?

기본적으로 Unity가 .NET으로 넘어가면서 발생하는 부수 효과로 GC가 바뀌는 것이며, GC가 메인은 아니라는 점을 이야기했음. 그러나 가치를 찾아보자면...

  • Compation이 지원 가능하므로, 메모리 파편화가 줄어듬

  • GC 비용을 예측 가능하게 만들 수 있음

댓글 작성

게시글에 대한 의견을 남겨 주세요.

댓글 0