C # / MonoGame - уничтожение больших объектов в памяти при разгрузке игрового уровня - PullRequest
1 голос
/ 08 октября 2019

Я работаю над игрой, использующей C # / MonoGame, и мне интересно, как решить проблему сбора мусора, касающуюся больших игровых объектов в памяти.

Каждый раз, когда я загружаю новую игру, я создаюи сохраните объект World, который сам имеет приватное поле, содержащее квадродерево для моей системы местности LOD. Quadtree рекурсивно содержит информацию о вершинах для всех его возможных дочерних узлов, вплоть до минимального уровня, который я решил, что хочу. Это означает, что каждый новый World занимает ~ 10 секунд для создания (выполняется в фоновом потоке) и имеет размер ~ 150 МБ в ОЗУ.

Я хотел бы быть уверен, что каждый раз, когда я загружаю новыйигра, старый World утилизируется, но, насколько я могу судить прямо сейчас, этого никогда не происходит. Я только что проверил это, нажав «загрузить игру» шесть раз подряд, и память, используемая моей игрой, касалась 1 ГБ, никогда не падая.

Я знаю, это может означать, что я все еще ссылаюсь на свою старую World объектов, но я не вижу, как это происходит. У моего основного игрового приложения есть один приватный World, который создается заново с нуля как новый объект в моей ветке загрузчика фона каждый раз, когда я нажимаю «загрузить игру»:

// This belongs to the game and is referenced when I want to interact with and draw the current World
private World _world;
.
.
// Executes on background loader thread when I press 'load game'
LoadGame()
{
    // This is where the old World is replaced with the new one, so I want the old one to be disposed as it contains ~150MB of useless data
    _world = new World(worldParameters);
}
.
.
Draw()
{
    // Rendering the active World in our draw loop
    DrawWorld(_world);
}

Я пытался установить _world обнулить и заставить GC собирать в начале метода LoadGame(), но это ничего не сделало.

Есть ли способ заставить память освободить старый объект или даже простопосмотреть, не указал ли я случайно на какое-либо из его полей в остальной части моей игры, чтобы оно оставалось в живых?

1 Ответ

0 голосов
/ 09 октября 2019

Распределение памяти может быть сложной задачей в таких языках, как C #. По иронии судьбы, эти языки разработаны так, что вам не нужно слишком много думать о распределении памяти. К сожалению, для игр с включенным сборщиком мусора во время игры может быть неприятный опыт для игрока, если для вашей игры важно поддерживать постоянную частоту кадров.

Есть два подхода, которые я знаю, чтобы иметь делос этим вопросом. Во-первых, вы должны явно вызывать сборщик мусора самостоятельно.

GC.Collect();
GC.WaitForPendingFinalizers();

Имейте в виду, это быстрый и грязный подход, который может не сработать так, как вы ожидаете. Многие люди в Интернете скажут вам, что не рекомендуется или плохая идея форсировать сборку мусора. Я не являюсь экспертом в плюсах и минусах, но я просто хотел, чтобы вы знали, что это вариант, чтобы вы могли провести собственное исследование.

Второй подход заключается в умном подходе к вашим структурам данных. Некоторые круги могут назвать это «реализацией собственной стратегии выделения памяти». По сути, все сводится к тому, чтобы заранее выделить большой кусок памяти и многократно использовать это пространство.

То, как выглядит точная стратегия для вас, может быть очень простым или очень сложным. Мой совет - начать с самой простой вещи, с которой вы можете сойти с рук, и посмотреть, как это происходит. Один из шаблонов, который может быть полезен для изучения, часто называют «объединением объектов».

...