Как игры C ++ обрабатывают сбой выделения памяти?

23

Мне известно о нескольких играх, написанных на C ++, но не использующих исключения. Так как обработка ошибки выделения памяти в C ++ обычно строится вокруг std::bad_allocисключения, как эти игры справляются с такой ошибкой?

Они просто аварийно завершают работу или есть другой способ обработки и восстановления после ошибки нехватки памяти?

user200783
источник
1
Обратите внимание, что есть определенные среды / ОС, в которых выделение утверждает, что оно
прошло
6
Если во время игры происходит сбой распределения, Quake 3 вернет вас обратно в меню с сообщением об ошибке. Я считаю, что это стало возможным благодаря распределителю пулов в Quake 3, который может просто отбросить весь пул и безопасно вернуться в меню, если пул закончится.
Дитрих Эпп
16
Обычно сбой.
user253751
1
Если исключения действительно отключены, программа должна вместо этого вызвать std::terminate, и это все. Но тогда, с тем же ограничением, распределение может возвращать нулевой указатель вместо исключения, и этот результат может быть проверен и обработан отдельно.
underscore_d
2
В C ++ вы можете сказать ClassName variableName = new(nothrow) ClassName();( очевидно, заменяя имя класса, имя переменной и т. Д. ). Затем, если распределение завершится неудачно, вы можете обнаружить это, сказав, if(!variableName)что ошибка может быть обработана без блока исключения try-catch. Точно так же, если память выделяется с использованием функции , как malloc(), calloc()и т.д., то отказы выделить могут быть обнаружены с использованием того же if(!variableName)метода без необходимости Try-улов. Что касается того, как игры справляются с этими ошибками, ну, на этом этапе, разработчики игры должны решить, сбоит она или нет.
Спенсер D

Ответы:

63

То же, что и все средние программы: нет. *

Для большинства приложений не ожидается, что они продолжат работать после исчерпания памяти. Все игры подпадают под эти "большинство приложений". Тратить время и деньги на работу в крайнем случае, который клиент не ожидает, не имеет смысла.

Вопрос похож на следующее:

  • Как установить игру, если жесткий диск заполнен?
  • Как вы все еще запускаете игру на высоких fps на ПК ниже минимальных характеристик?

Ответ тот же: это проблемы пользователей. Игра не волнует.


* На самом деле, они обычно делают высокий уровень перехвата исключения и используют память, которая была предварительно выделена в начале игры, чтобы попытаться записать событие перед сбоем / завершением. Журнал позволяет службе поддержки тратить меньше времени на решение проблемы.

Питер - Унбан Роберт Харви
источник
2
О предварительном выделении памяти для ведения журнала и т. Д. В случае сбоя выделения памяти: stackoverflow.com/questions/7749066/…
bwDraco
23

Как правило, такого рода сценарий никогда не происходит.

Во-первых, виртуальная память в современных операционных системах означает, что в любом случае это вряд ли произойдет при нормальной работе; если только у вас не будет ошибки размещения, игра будет выделять память из виртуального адресного пространства ОС, а ОС будет следить за входом и выходом.

Это все хорошо, но на самом деле это не относится к консолям или старым ОС, поэтому, во-вторых, игры в любом случае даже не выполняют много небольших динамических распределений, используя стандартные распределители библиотек. Вместо этого они будут выделять большой пул памяти только один раз при запуске и использовать этот пул для любых требуемых выделений во время выполнения (например, с помощью нового размещения C ++ или написания собственной системы управления памятью в вершина этого).

Это также защищает от утечек памяти, потому что вместо того, чтобы отслеживать и учитывать каждое маленькое отдельное распределение, игра может просто выбросить весь пул для восстановления памяти.

И в-третьих, игры всегда будут устанавливать минимальные спецификации и планировать использование памяти. Таким образом, если для игры требуется 1 ГБ ОЗУ, это означает, что до тех пор, пока ее использование памяти никогда не превышает 1 ГБ, ей не нужно беспокоиться об исчерпании ОЗУ.

Максимус Минимус
источник
3
«Вместо этого они будут выделять большой пул памяти только один раз при запуске и использовать из этого пула все необходимые выделения времени выполнения» - и что, по вашему мнению, происходит, если пул заполнен?
user253751
@immibis - см. мой третий пункт, пожалуйста.
Максимус Минимус
2
@immibis Вы имеете в виду ... что они делают, если бассейн пуст? В отличие от системной памяти, размер и использование пула полностью находятся под контролем приложения. Таким образом, пул не может стать пустым, если в приложении нет ошибки.
Дэвид Шварц
@DavidSchwartz Или, если ядро ​​не использует избыточную память. Linux делает это. Даже обнуление пула не поможет, если сжатие файла подкачки включено через zswap или zram.
Дамиан Йеррик
1
Похоже, что это не отвечает на настоящий вопрос, но вместо этого заявляет что-то похожее на «Этот корабль непотопляем, так зачем нам нужна экстренная процедура?
Лилиенталь
2

Ну, в основном, так же, как мы делали это до появления исключений - старый метод «проверьте возвращаемое значение». Если распределитель не использует исключения, он обычно возвращается nullпри сбое выделения. Хорошо написанная игра проверит это и разрешит ситуацию любым безопасным способом. Иногда это означает прекращение игры (например,std::terminate ) или, по крайней мере, возврат к последнему известному безопасному состоянию (например, когда вы выделяете из пула, вы можете безопасно утилизировать весь пул, даже когда он находится в небезопасном состоянии).

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

Имейте в виду, большинство игр просто рухнет. Это не так безумно, как кажется - у вас обычно есть некоторое использование памяти в качестве цели, и убедитесь, что ваша игра не достигает этого предела (например, используя ограничение количества юнитов в игре RTS или ограничивая количество врагов в разделе FPS). Даже если вы этого не сделаете, у вас обычно не хватает памяти в какой-либо достаточно современной системе - например, у вас заканчивается виртуальное адресное пространство (в 32-разрядном приложении) или стек. ОС уже делает вид, что у вас столько памяти, сколько вы просите - это одна из абстракций, которую предоставляет виртуальная память. Дело в том, что наличие 256 МБ физической памяти не означает, что ваше приложение не может использовать 4 ГБ памяти. Некоторые игры могут даже не возражать, что все их данные не

Luaan
источник
1

Поймать исключение и решить, что, когда и когда возникает исключение, это две разные вещи.

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

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

Дакш
источник
Если рассматриваемые игры буквально «не используют исключения», как сказал ОП, то они не могут поймать, поднять или обработать одно. Если исключения отключены, и кто-то выдает их, программа должна вместо этого вызвать std::terminate, и это все. Но в таких условиях распределение может возвращать нулевой указатель вместо исключения, и это может быть обработано отдельно.
underscore_d