К счастью, как вы указали, сборки COMPACT Mono используют GC поколения (в отличие от Microsoft, например, WinMo / WinPhone / XBox, которые просто поддерживают плоский список).
Если ваша игра проста, GC должен справиться с ней просто отлично, но вот несколько советов, которые вы могли бы рассмотреть.
Преждевременная оптимизация
Сначала убедитесь, что это действительно проблема для вас, прежде чем пытаться ее исправить.
Объединение дорогих справочных типов
Вам следует объединить ссылочные типы, которые вы часто создаете или которые имеют глубокие структуры. Примером каждого будет:
- Создано часто:
Bullet
объект в игре пуля-ада .
- Глубокая структура: дерево решений для реализации ИИ.
Вы должны использовать в Stack
качестве пула (в отличие от большинства реализаций, которые используют Queue
). Причина этого в том, что Stack
если вы возвращаете объект в пул, и что-то еще немедленно его захватывает; у него будет гораздо больше шансов оказаться на активной странице - или даже в кэше процессора, если вам повезет. Это немного быстрее. Кроме того, всегда ограничивайте размер ваших пулов (просто игнорируйте 'checkins', если ваш лимит был превышен).
Избегайте создания новых списков, чтобы очистить их
Не создавайте новое, List
когда вы на самом деле имели в виду Clear()
это. Вы можете повторно использовать внутренний массив и сохранить загрузку массивов и копий. Подобно этому, попробуйте создать списки с осмысленной начальной емкостью (помните, что это не предел - просто начальная емкость) - она не должна быть точной, просто оценочной. Это должно относиться в основном к любому типу коллекции - за исключением LinkedList
.
Используйте массивы структур (или списки), где это возможно
Вы получаете небольшую выгоду от использования структур (или типов значений в целом), если вы передаете их между объектами. Например, в большинстве «хороших» систем частиц отдельные частицы хранятся в массиве: массив и индекс передаются вместо самой частицы. Причина, по которой это работает так хорошо, заключается в том, что когда GC нужно собрать массив, он может полностью пропустить содержимое (это примитивный массив - здесь ничего не нужно делать). Поэтому вместо 10 000 объектов GC просто нужно посмотреть на 1 массив: огромный выигрыш! Опять же, это будет работать только с типами значений .
После РойТ. предоставив некоторую жизнеспособную и конструктивную обратную связь, я чувствую, что мне нужно подробнее остановиться на этом. Вы должны использовать эту технику только тогда, когда имеете дело с огромным количеством сущностей (от тысяч до десятков тысяч). Кроме того, это должна быть структура, которая не должна иметь никаких полей ссылочного типа и должна находиться в массиве с явным типом. Вопреки его отзывам, мы помещаем его в массив, который, скорее всего, является полем в классе - это означает, что он собирается занять кучу (мы не пытаемся избежать выделения кучи - просто избегаем работы GC). Мы действительно заботимся о том, что это непрерывный кусок памяти с большим количеством значений, которые GC может просто посмотреть в O(1)
операции вместо O(n)
операции.
Вам также следует распределить эти массивы как можно ближе к запуску приложения, чтобы уменьшить вероятность возникновения фрагментации или чрезмерной работы, когда GC пытается переместить эти фрагменты (и рассмотрите возможность использования гибридного связанного списка вместо встроенного List
типа). ).
GC.Collect ()
Это, безусловно, ЛУЧШИЙ способ выстрелить себе в ногу (см. «Вопросы производительности») с помощью GC поколения. Вы должны вызывать его только тогда, когда вы создали ОЧЕНЬ большое количество мусора - и единственный случай, когда это может быть проблемой, - это сразу после загрузки контента для уровня - и даже тогда вам, вероятно, следует собирать только первое поколение ( GC.Collect(0);
) Надеемся, что предотвратить продвижение объектов в третьем поколении.
IDisposable и обнуление поля
Стоит обнулять поля, когда вам больше не нужен объект (особенно на ограниченных объектах). Причина в деталях того, как работает GC: он удаляет только те объекты, которые не являются корневыми (то есть ссылаются), даже если этот объект был бы удален из-за удаления других объектов в текущей коллекции ( примечание: это зависит от GC аромат в использовании - некоторые действительно убирают цепи). Кроме того, если объект переживает коллекцию, он немедленно продвигается до следующего поколения - это означает, что любые объекты, оставленные в полях, будут продвигаться во время коллекции. Каждое последующее поколение экспоненциально дороже собирать (и это происходит нечасто).
Возьмите следующий пример:
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// G1 Collection
MyNestObject (G2) -> MyFurtherNestedObject (G2)
// G2 Collection
MyFurtherNestedObject (G3)
Если вы MyFurtherNestedObject
содержали объект размером в несколько мегабайт, вы можете быть уверены, что ГХ не будет смотреть на него в течение достаточно долгого времени - потому что вы непреднамеренно повысили его до 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;
}
}