운 좋게도 COMPACT Mono 빌드는 세대 별 GC를 사용합니다 (단순한 목록을 유지하는 WinMo / WinPhone / XBox와 같은 Microsoft와는 대조적 임).
게임이 단순하다면 GC는 잘 처리해야하지만 여기에 몇 가지 포인터가 있습니다.
조기 최적화
먼저 문제를 해결하기 전에 이것이 실제로 문제인지 확인하십시오.
풀링 비싼 참조 유형
자주 작성하거나 구조가 깊은 참조 유형을 풀링해야합니다. 각각의 예는 다음과 같습니다.
- 자주 생성 : 총알 게임 의
Bullet
객체 .
- 심층 구조 : AI 구현을위한 의사 결정 트리.
을 사용하는 Stack
대부분의 구현과 달리을 풀로 사용해야합니다 Queue
. 그 이유 Stack
는 객체를 풀에 반환하면 다른 객체가 즉시 객체를 가져 오기 때문입니다. 활성 페이지 또는 운이 좋으면 CPU 캐시에있을 가능성이 훨씬 높습니다. 아주 조금 더 빠릅니다. 또한 항상 수영장의 크기를 제한하십시오 (한도를 초과 한 경우 '체크인'은 무시하십시오).
삭제하기 위해 새 목록을 작성하지 마십시오
List
실제로 새로운 것을 만들지 마십시오 Clear()
. 백엔드 어레이를 재사용하고 어레이 할당 및 사본로드를 저장할 수 있습니다. 이 시도와 마찬가지로 초기 용량이 의미있는 목록을 작성하십시오 (이것은 제한이 아니며 시작 용량 임). 정확할 필요는 없으며 추정 일뿐입니다. 이것은 기본적으로 a를 제외한 모든 컬렉션 유형에 적용됩니다 LinkedList
.
가능한 경우 구조 배열 (또는 목록)을 사용하십시오.
객체간에 구조체를 전달하면 사용 구조체 (또는 일반적으로 값 형식)의 이점이 거의 없습니다. 예를 들어, 대부분의 '좋은'입자 시스템에서 개별 입자는 방대한 배열에 저장됩니다. 배열과 색인은 입자 자체 대신 전달됩니다. 이것이 잘 작동하는 이유는 GC가 배열을 수집해야 할 때 내용을 완전히 건너 뛸 수 있기 때문입니다 (기본 배열이므로 여기서 할 일이 없습니다). 따라서 10,000 개의 객체를 보는 대신 GC는 단순히 1 개의 배열을보아야합니다. 다시 말하지만, 이는 value 유형에서만 작동합니다 .
RoyT 후. 실행 가능하고 건설적인 피드백을 제공하여 더 확장해야한다고 생각합니다. 방대한 양의 엔티티 (수만에서 수만)를 다룰 때만이 기술을 사용해야합니다. 또한이 있어야 구조체를 수있는 하지해야 모든 참조 형식 필드를 가지고 명시 적으로 형식의 배열에 거주해야합니다. 그의 의견과 달리 우리는 클래스의 필드 일 가능성이 높은 배열에 배열을 배치합니다. 즉 힙을 쌓을 것입니다 (힙 할당을 피하려는 것이 아니라 단순히 GC 작업을 피하려고 함). 우리는 실제로 GC가 단순히 O(1)
작업 대신 작업 에서 볼 수있는 많은 값을 가진 연속적인 메모리 덩어리라는 사실에 관심을 기울 O(n)
입니다.
또한 이러한 배열을 가능한 한 응용 프로그램 시작에 가깝게 할당하여 조각화가 발생할 가능성을 완화 하거나 GC가 이러한 청크를 이동하려고 할 때 과도한 작업을 수행해야합니다 ( 내장 유형 대신 하이브리드 연결 목록 사용을 고려 하십시오) List
).
GC.Collect ()
이것은 세대 별 GC 를 사용하여 발 에 쏠 수있는 가장 좋은 방법입니다 ( "성능 고려 사항"참조). EXTREME 가비지를 생성 한 경우에만 호출해야 하며, 문제가 될 수있는 경우는 레벨의 컨텐츠를로드 한 직후입니다. 심지어 1 세대 만 수집해야합니다 (GC.Collect(0);
) 3 세대로 객체를 홍보하는 것을 막기 위해
IDisposable 및 필드 널링
더 이상 객체가 필요하지 않은 경우 (제한된 객체의 경우) null 필드를 사용하는 것이 좋습니다. 그 이유는 GC 작동 방식에 대한 세부 사항에 있습니다. 현재 컬렉션에서 다른 객체가 제거되어 해당 객체가 루팅되지 않은 경우에도 루팅되지 않은 (즉, 참조 된) 객체 만 제거합니다 ( 주 : GC에 따라 다름) 사용하는 맛-일부는 실제로 체인을 청소합니다). 또한 개체가 컬렉션에 남아 있으면 즉시 다음 세대로 승격됩니다. 즉, 필드에 남아있는 모든 개체는 컬렉션 중에 승격됩니다. 각각의 연속적인 세대는 기하 급수적으로 비용이 많이 들고 비 정기적으로 발생합니다.
다음 예를 보자.
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// G1 Collection
MyNestObject (G2) -> MyFurtherNestedObject (G2)
// G2 Collection
MyFurtherNestedObject (G3)
MyFurtherNestedObject
멀티 메가 바이트 객체가 포함되어 있으면 GC가 오랫동안이를 보지 않을 수 있습니다. 실수로 G3으로 승격했기 때문입니다. 이 예제와 대조하십시오 :
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// Dispose
MyObject (G1)
MyNestedObject (G1)
MyFurtherNestedObject (G1)
// G1 Collection
Disposer 패턴을 사용하면 개체가 개인 필드를 지우도록 요청하는 예측 가능한 방법을 설정할 수 있습니다. 예를 들면 다음과 같습니다.
public class MyClass : IDisposable
{
private MyNestedType _nested;
// A finalizer is only needed IF YOU CONTROL UNMANAGED RESOURCES
// ~MyClass() { }
public void Dispose()
{
_nested = null;
}
}