할당 줄이기(문자열 합치기·LINQ·람다 캡처 피하기), 오브젝트 풀, 점진적 GC 켜 두기(유니티 2020 이후 기본값), 로딩 화면처럼 멈춰도 괜찮은 때에 미리 GC 돌리기.
수치 감각
보통 한 번에 수 ms~100ms, 저사양 폰이나 메모리를 많이 쓰는 게임은 그 이상(실험은 약 150~170ms). 유니티의 GC는 돌 때마다 힙 전체를 검사해서 게임이 쓰고 있는 메모리가 많을수록 길어집니다.
그래프에서는
일정 주기로 튐 · 프레임 시간, GC 실행 시각
확인할 곳
개발 빌드에서 유니티 프로파일러의 GC.Collect·GC.Alloc 마커를 봄. 언리얼은 stat GC와 stat Hitches(t.HitchFrameTimeThreshold로 정한 시간을 넘는 프레임을 로그로 남김)
이러면 맞음
튄 프레임마다 GC.Collect 구간이 있고 그 길이가 튄 길이와 비슷하며, 간격이 몇 초~몇십 초로 규칙적임. 사람 많은 곳에서 프레임당 GC.Alloc이 늘어남
이러면 아님
튄 프레임에 GC 구간이 없으면 “메인 스레드 동기 로딩·셰이더 컴파일”이나 “대규모 인원 렌더링 부하”
확인 수단
게임 서버·클라이언트의 로그·지표가 필요
더 알아보기
유니티처럼 C#을 쓰는 클라이언트에서 흔합니다. 전투 로그 문자열, 데미지 숫자, UI 텍스트를 매 프레임 새로 만드는 코드가 가장 흔한 원인입니다. 사람 많은 곳에서만 끊긴다면 인원에 비례해 가비지가 늘어나는 코드가 원인입니다. 점진적 GC는 한 프레임에 조금씩(유니티 기본 3ms) 나눠 수집하지만 수집하는 속도보다 가비지를 빨리 만들면 결국 한 번에 멈춥니다. 언리얼 엔진에도 안 쓰는 게임 오브젝트를 정리하는 자체 GC가 있고 엔진 버전·설정에 따라 다르지만 기본 설정으로 약 1분마다 돌아 1분 간격의 멈칫을 만들기도 합니다. 게임 규칙을 Lua 같은 스크립트로 짠 클라이언트는 그 스크립트의 GC도 따로 돕니다.
출처
Garbage collection modesUnity 점진적 GC가 기본값이고 여러 프레임에 나눠 수집, 끄면 힙 전체를 검사하는 동안 메인 스레드가 멈추며 수백 ms까지 갈 수 있음