가비지 수집 성능 향상

C# 및 Visual Basic으로 작성한 UWP(Universal Windows Platform) 앱은 .NET 가비지 수집기의 자동 메모리 관리를 사용합니다. 이 섹션은 UWP 앱의 .NET 가비지 수집기에 대한 동작 및 성능 모범 사례를 요약합니다. .NET 가비지 수집기의 작동 방식과 가비지 수집기 성능 디버깅 및 분석용 도구에 대한 자세한 내용은 가비지 수집을 참조하세요.

참고 가비지 수집기의 기본 동작에 개입해야 한다는 것은 앱의 일반적인 메모리 문제를 강력하게 나타냅니다. 자세한 정보는 Visual Studio 2015에서 디버그하는 동안의 메모리 사용량 도구를 참조하세요. 이 항목은 C# 및 Visual Basic에만 적용됩니다.

 

가비지 수집기는 관리되는 힙의 메모리 소비를 가비지 수집을 위해 수행해야 하는 작업의 양과 균형을 맞춤으로써 실행할 시점을 결정합니다. 가비지 수집기가 이 작업을 수행하는 방법 중 하나는 힙을 세대로 구분하고 대부분 힙의 일부만 수집하는 것입니다. 관리되는 힙으로는 다음의 세 개 세대가 있습니다.

  • 0세대. 이 세대는 큰 개체 힙의 일부로서 85KB보다 작은 새로 할당된 개체를 포함합니다. 큰 개체 힙은 세대 2 수집으로 수집됩니다. 0세대 수집은 가장 자주 발생하는 수집 유형이고 로컬 변화와 같이 수명이 짧은 개체를 정리합니다.
  • 1세대. 이 세대는 0세대 수집에서 정리되지 않은 개체를 포함합니다. 이는 0세대 및 2세대 사이의 버퍼로 작용합니다. 1세대 수집은 0세대 수집보다 발생 빈도가 낮으며 이전 0세대 수집 중에 활성화된 임시 개체를 정리합니다. 1세대 수집은 0세대도 수집합니다.
  • 2세대. 이 세대는 0세대 및 1세대 수집에서 정리되지 않은 수명이 긴 개체를 포함합니다. 2세대 수집은 수행 빈도가 제일 낮으며 85KB 이상인 개체가 포함된 큰 개체 힙을 비롯한 전체 관리되는 힙을 수집합니다.

가비지 수집기의 성능은 가비지 수집을 수행하는 데 걸린 시간 및 관리되는 힙의 메모리 사용이라는 2가지 측면에서 측정할 수 있습니다. 힙 크기가 100MB 미만인 작은 앱이 잇는 경우 메모리 사용 감소에 집중합니다. 100M를 초과하는 관리되는 힙이 있는 앱이 있을 경우 가비지 수집 시간을 줄이는 데 집중합니다. 다음은 .NET 가비지 수집기의 성능을 향상시킬 수 있는 방법입니다.

메모리 사용 감소시키기

릴리스 참조

개체에 대한 참조가 앱에 있으면 개체 및 개체가 참조하는 모든 개체가 수집되지 않습니다. .NET 컴파일러는 변수가 더 이상 사용되지 않는 경우를 감지하므로 해당 변수를 통해 유지되는 개체를 수집할 수 있습니다. 그러나 경우에 따라 개체 그래프의 일부가 앱에서 사용하는 라이브러리에 소유될 수 있으므로 특정 개체에 다른 개체에 대한 참조가 있는지 분명하지 않을 수 있습니다. 가비지 수집에서 정리되지 않는 개체를 찾는 도구와 기술을 알아 보려면 가비지 수집 및 성능을 참조하세요.

유용한 경우 가비지 수집 유도하기

앱 성능을 측정했으며 수집 유도가 성능을 개선할 것으로 판단된 후에만 가비지 수집을 유도합니다.

GC.Collect(n)을 호출하여 세대에 대한 가비지 수집을 유도할 수 있습니다. 여기서 n은 수집할 세대입니다(0, 1 또는 2).

참고 가비지 수집기에서 많은 경험적 방법을 사용하여 수집을 수행하는 데 가장 적합한 시간을 결정하고 수집을 강제하는 것은 대부분의 경우 CPU를 불필요하게 사용하므로 앱에서 가비지 수집을 강제로 수행하지 않는 것이 좋습니다. 그러나 더 이상 사용되지 않는 상당한 개체가 앱에 있다는 것을 알고 있고 이 메모리를 시스템으로 반환하려는 경우에는 가비지 수집을 강제 실행하는 것이 적절할 수 있습니다. 예를 들어 게임에서 로드 순서가 끝날 때 수집을 유도하여 게임 실행이 시작되기 전에 메모리를 해제할 수 있습니다.   너무 많은 가비지 수집을 실수로 유도하지 않으려면 GCCollectionModeOptimized로 설정합니다. 이렇게 하면 수집이 정당화하기에 충분히 생산적인 것으로 판단되는 경우에만 가비지 수집기가 수집을 시작합니다.

가비지 수집 시간 단축하기

이 섹션은 앱을 분석하고 오랜 시간 가비지 수집을 관찰한 경우에 적용됩니다. 가비지 수집 관련 일시 중지 시간에는 단일 가비지 수집 단계를 실행하는 데 걸리는 시간, 그리고 앱이 가비지 수집에 소요하는 총 시간이 있습니다. 수집에 걸리는 시간은 수집기가 분석해야 하는 데이터의 수명에 따라 결정됩니다. 0세대 및 1세대는 크기가 제한되지만 2세대는 앱에서 활성 상태인 수명이 긴 개체가 많을수록 계속 증가합니다. 따라서 0세대 및 1세대의 수집 시간은 제한되지만 2세대 수집에는 더 오랜 시간이 걸릴 수 있습니다. 가비지 수집은 메모리를 해제하여 할당 요청을 충족하므로 가비지 수집이 실행되는 빈도는 주로 할당하는 메모리 양에 따라 결정됩니다.

가비지 수집기는 가끔 작업을 수행하기 위해 앱을 일시 중지하지만 수집을 수행하는 내내 앱을 일시 중지할 필요는 없습니다. 일시 중지 시간은 대개 앱에서 사용자가 인식하지 못할 정도이며 특히 0세대 및 1세대 수집의 경우 그렇습니다. .NET 가비지 수집기의 백그라운드 가비지 수집 기능을 통해 앱을 실행하는 동시에 2세대 수집을 수행할 수 있으며 잠시 동안만 앱을 일시 중지할 수 있습니다. 그러나 항상 2세대 수집을 백그라운드 수집으로 수행할 수 있는 것은 아닙니다. 100MB를 초과하는 상당히 큰 힙이 있는 경우 사용자가 일시 중지를 인식할 수 있습니다.

가비지 수집을 자주 실행하면 CPU 및 전원 사용이 증가하거나 로드 시간이 길어지거나 애플리케이션의 프레임 속도가 느려질 수 있습니다. 아래는 관리되는 UWP 앱에서 가비지 수집 시간 및 수집 관련 일시 중지를 줄이는 데 사용할 수 있는 기술입니다.

메모리 할당 감소시키기

개체를 할당하지 않으면 시스템의 메모리가 부족한 경우를 제외하고 가비지 수집기가 실행되지 않습니다. 가비지 수집 빈도는 할당할 메모리 양을 줄이면 감소합니다.

앱의 특정 섹션에서 일시 중지가 발생하지 않아야 하는 경우에는 성능에 대한 부담이 적은 시간에 미리 필요한 개체를 할당할 수 있습니다. 예를 들어 게임에서 한 수준의 화면을 로드하는 동안 게임 실행에 필요한 모든 개체를 할당하고 게임 실행 중에는 할당을 수행하지 않을 수 있습니다. 이렇게 하면 사용자가 게임을 실행하는 동안 일시 중지가 발생하지 않으므로 더 높고 더 일관된 프레임 속도가 제공됩니다.

수명이 중간 정도인 개체를 방지하여 2세대 수집 줄이기

세대 가비지 수집은 실제로 수명이 짧은 개체나 수명이 긴 개체가 앱에 있는 경우 가장 적합합니다. 수명이 짧은 개체는 부담이 적은 0세대 및 1세대 수집에서 수집되고 수명이 긴 개체는 드물게 수집되는 2세대로 승격됩니다. 수명이 긴 개체는 앱의 전체 수명 동안 사용되거나 특정 페이지나 게임 수준과 같이 앱의 상당한 기간 동안 사용되는 개체입니다.

수명이 일시적이지만 2세대로 승격될 만큼 오래 유지되는 개체를 자주 만들면 부담이 큰 2세대 수집이 더 많이 발생합니다. 기존 개체를 재활용하거나 개체를 더 빠르게 해제하면 2세대 수집을 줄일 수 있습니다.

중간 기간 수명 개체의 일반적인 예는 대개 사용자가 스크롤하는 목록에 항목을 표시하는 데 사용되는 개체입니다. 개체가 목록의 항목이 보기로 스크롤될 때 생성되고 스크롤하는 목록의 항목이 보기를 벗어나면 더 이상 참조되지 않을 경우, 대개 많은 2세대 수집이 앱에 발생합니다. 이와 같은 경우에는 사용자에게 자주 표시되는 데이터에 대해 일련의 개체를 미리 할당하고 다시 사용하며 수명이 짧은 개체를 사용하여 목록의 항목이 보기에 나타날 때 정보를 로드할 수 있습니다.

수명이 짧은 큰 개체를 방지하여 2세대 수집 줄이기

85KB 이상인 개체는 LOH(large object heap)에 할당되고 2세대의 일부로 수집됩니다. 버퍼와 같이 85KB보다 큰 임시 변수가 있는 경우, 2세대 수집이 이러한 변수를 정리합니다. 임시 변수를 85KB 미만으로 제한하면 앱에서 2세대 수집 횟수가 감소합니다. 한 가지 일반적인 방법은 버퍼 풀을 만들고 풀의 개체를 다시 사용하여 대규모 임시 할당을 피하는 것입니다.

참조가 많은 개체 방지하기

가비지 수집기는 앱의 루트부터 개체 간 참조에 따라 유지할 개체를 결정합니다. 자세한 정보는 가비지 수집 중에 수행되는 작업을 참조하세요. 개체에 여러 참조가 포함된 경우에는 가비지 수집기가 수행할 작업이 더 많습니다. (특히 큰 개체인 경우) 일반적인 방법은 참조가 많은 개체를 참조가 없는 개체로 변환하는 것이므로, 참조를 저장하지 않고 인덱스를 저장합니다. 물론 논리적으로 가능한 경우에만 이 방법이 작동됩니다.

개체 참조를 인덱스로 바꾸는 작업은 앱에 지장을 주고 복잡한 변경일 수 있으므로 상당한 참조가 포함된 큰 개체의 경우에 가장 효과적입니다. 이 작업은 참조가 많은 개체에 관련된 앱에서 상당한 가비지 수집 횟수가 발견된 경우에만 수행하세요.