점진적 가비지 컬렉션(GC)은 가비지 컬렉션 프로세스를 여러 프레임에 분산시킵니다. 이는 Unity에서 가비지 컬렉션 동작의 기본값입니다.
Unity의 가비지 컬렉터는 Boehm–Demers–Weiser 가비지 컬렉터를 사용합니다. 기본적으로 Unity는 점진적 모드에서 이를 사용합니다. 즉, 가비지 컬렉터는 관리되는 힙의 모든 오브젝트를 처리하기 위해 메인 CPU 스레드를 중지(stop-the-world garbage collection)하는 대신 작업량을 여러 프레임에 걸쳐 분할합니다. 즉, Unity는 가비지 컬렉터가 관리되는 힙에서 오브젝트를 처리할 수 있도록 한 번의 긴 중단 대신 애플리케이션의 실행을 더 짧게 중단합니다.
점진적 모드는 전반적으로 가비지 컬렉션을 더 빠르게 만들지 않지만 여러 프레임에 작업량을 분산하기 때문에 GC 관련 성능 스파이크가 줄어듭니다. 이러한 중단은 프로파일러 창 프레임 시간 그래프에서 큰 스파이크로 나타나기 때문에 GC 스파이크라고 합니다.
점진적 모드(메뉴: Edit > Project Settings > Player > Other Settings > Configuration > Use Incremental GC)를 비활성화하는 경우 가비지 컬렉터는 컬렉션을 전달할 때 전체 힙을 검사해야 합니다. 이것은 stop-the-world 방식의 가비지 컬렉션으로 알려져 있습니다. 가비지 컬렉터가 실행될 때마다 메인 CPU 스레드를 중지하기 때문입니다. 관리되는 힙의 모든 오브젝트를 처리한 후에만 실행을 재개하므로 GC 스파이크가 애플리케이션 성능에 영향을 줄 수 있습니다. 또한 가비지 컬렉터는 압축되지 않습니다. 즉, Unity는 오브젝트를 압축하기 위해 메모리에 있는 오브젝트를 재배포하지 않습니다.
중요: WebGL 플랫폼은 점진적 가비지 컬렉션을 지원하지 않습니다.
점진적 가비지 컬렉션이 비활성화되면 Unity가 가비지 컬렉션을 수행하기 위해 프로그램 코드 실행을 중지하게 되고 이때 GC 스파이크가 발생합니다. 이러한 지연은 가비지 컬렉터에서 처리해야 하는 할당 수와 애플리케이션이 실행 중인 플랫폼에 따라 수백 밀리초 동안 지속될 수 있습니다.
가비지 컬렉터가 애플리케이션의 실행을 일시 중지할 때 원활한 애니메이션에 필요한 일관된 프레임 속도를 유지하기가 어렵기 때문에 게임과 같은 실시간 애플리케이션에서는 문제가 됩니다.
다음 프로파일러 스크린샷은 점진적 가비지 컬렉션이 프레임 속도 문제를 줄이는 방법을 보여줍니다.
위쪽 프로파일링 세션에서는 점진적 GC가 활성화되어 있습니다. 가비지 컬렉터가 가비지 컬렉션 작업을 여러 프레임에 걸쳐 분산하고 각 프레임의 작은 시간 조각(노란색 VSync 트레이스 바로 위에 있는 진한 녹색 테두리)을 사용하기 때문에 애플리케이션은 일관된 60fps 프레임 속도를 가집니다.
아래쪽 프로파일링 세션에서는 점진적 GC가 비활성화되어 있으며 명확한 GC 스파이크가 보입니다. 이 스파이크는 원활한 60fps 프레임 속도를 방해하고 가비지 컬렉션이 일어나는 프레임이 60fps를 유지하는 데 필요한 16밀리초 제한을 초과하도록 만듭니다.
애플리케이션에서 VSync 또는 Application.targetFrameRate를 사용하는 경우, Unity는 사용 가능한 남은 프레임 시간을 기준으로 가비지 컬렉션에 할당하는 시간을 조정합니다. Unity는 이러한 방식으로 대기 시간에 가비지 컬렉션을 실행할 수 있으며 성능에 미치는 영향을 최소화하면서 가비지 컬렉션을 수행할 수 있습니다.
참고:프로젝트 품질 설정에서 VSync Count를 Don’t Sync 이외의 옵션으로 설정하거나 Application.VSync 프로퍼티 또는 Application.targetFrameRate 프로퍼티를 설정하면, Unity는 특정 프레임이 끝날 때 남은 대기 상태 시간을 점진적 가비지 컬렉션에 자동으로 사용합니다.
점진적 가비지 컬렉션 동작을 더욱 정밀하게 제어하기 위해 Scripting.GarbageCollector 클래스를 사용할 수 있습니다. 예를 들어 VSync 또는 타겟 프레임 속도를 사용하고 싶지 않으면 프레임이 끝나기 전에 직접 이용 가능한 시간을 계산한 후 해당 값을 가비지 컬렉터에 제공하여 사용하도록 만들 수 있습니다.
점진적 가비지 컬렉션은 가비지 컬렉터가 점진적 모드에서 작업량을 분할할 때 마킹 단계도 분할하기 때문에 애플리케이션에 문제가 될 수 있습니다. 마킹 단계는 가비지 컬렉터가 모든 관리되는 오브젝트를 스캔하여 사용 중인 오브젝트와 정리할 수 있는 오브젝트를 판단합니다.
마킹 단계 분할은 오브젝트 사이에 있는 레퍼런스 대다수가 작업 조각 사이에서 변하지 않을 때 특히 유용합니다. 그러나 오브젝트 레퍼런스가 변경되면 가비지 컬렉터가 다음 반복에서 해당 오브젝트를 다시 스캔해야 합니다. 즉 변경 사항이 너무 많으면 점진적 가비지 컬렉터에 과부하가 생길 수 있고 항상 처리해야 하는 작업이 너무 많아서 마킹 단계가 완료되지 않는 상황을 만들 수 있음을 의미합니다. 이런 일이 발생하면 가비지 컬렉터가 완전한 비점진적 컬렉션으로 폴백합니다.
점진적 가비지 컬렉션을 사용할 경우 레퍼런스가 변경될 때마다 Unity는 가비지 컬렉터에 알리는 추가 코드(쓰기 배리어라고 함)를 생성하여 오브젝트를 다시 스캔해야 하는지 여부를 알 수 있습니다. 레퍼런스를 변경할 때는 관리되는 코드에 성능 영향을 줄 수 있으므로 오버헤드가 추가로 발생합니다.
점진적 가비지 컬렉션을 비활성화하려면 플레이어 설정 창(Edit > Project Settings > Player > Configuration)을 열고 Use Incremental GC를 비활성화합니다. 대부분의 Unity 프로젝트는 특히 가비지 컬렉션 스파이크가 발생하는 경우 점진적 가비지 컬렉션의 이점을 누리고 있지만 항상 프로파일러를 사용하여 애플리케이션이 예상대로 작동하는지 확인해야 합니다.