Conozco varios juegos que están escritos en C ++ pero no usan excepciones. Dado que el manejo de la falla de asignación de memoria en C ++ generalmente se basa en la std::bad_alloc
excepción, ¿cómo manejan estos juegos tal falla?
¿Simplemente se bloquean o hay otra forma de manejar y recuperarse de un error de falta de memoria?
c++
resource-management
memory
usuario200783
fuente
fuente
std::terminate
, y eso es todo. Pero luego, bajo la misma restricción, la asignación puede devolver un puntero nulo en lugar de lanzar una excepción, y ese resultado se puede verificar y manejar por separado.ClassName variableName = new(nothrow) ClassName();
( obviamente, reemplazar el nombre de la clase, el nombre de la variable, etc. ) Luego, si la asignación falla, puede detectarlo diciendo queif(!variableName)
permite que el error se maneje sin un bloque de excepción try-catch. Del mismo modo, si la memoria se asigna usando una función comomalloc()
,calloc()
etc., entonces las fallas de asignación se pueden detectar usando el mismoif(!variableName)
método sin necesidad de un try-catch. En cuanto a cómo los juegos manejan estos errores, bueno, en ese punto, depende de los desarrolladores del juego decidir si se bloquea o no.Respuestas:
Igual que todos los programas promedio: no lo hacen. *
Para la mayoría de las aplicaciones, los clientes no esperan que continúen funcionando una vez que se agote la memoria. Todos los juegos se incluyen en estas "la mayoría de las aplicaciones". Gastar tiempo y dinero para trabajar en un caso límite que el cliente no espera que funcione no tiene sentido.
La pregunta es similar a la siguiente:
La respuesta es la misma: estos son los problemas de los usuarios. Al juego no le importa.
* En realidad, generalmente hacen una captura de alto nivel de la excepción, y usan memoria que fue preasignada al comienzo del juego para intentar registrar el evento antes de estrellarse / terminar. El registro luego permite que el servicio al cliente pierda menos tiempo en el tema.
fuente
Por lo general, este tipo de escenario nunca sucede.
En primer lugar, la memoria virtual en los sistemas operativos modernos significa que es muy poco probable que suceda en el funcionamiento normal de todos modos; a menos que tenga un error de asignación fuera de control, el juego asignará memoria desde el espacio de direcciones virtuales del sistema operativo y el sistema operativo se ocupará de la entrada y la salida.
Eso está muy bien, pero en realidad no se aplica a consolas o sistemas operativos más antiguos, por lo que, en segundo lugar, los juegos ni siquiera hacen muchas asignaciones dinámicas pequeñas utilizando los asignadores de biblioteca estándar de todos modos. En cambio, lo que harán es asignar un gran conjunto de memoria una sola vez al inicio, y extraer de ese conjunto las asignaciones de tiempo de ejecución que sean necesarias (por ejemplo, mediante la colocación de C ++ nueva o escribiendo su propio sistema de administración de memoria en arriba de eso).
Eso también protege contra pérdidas de memoria porque, en lugar de tener que rastrear y dar cuenta de cada pequeña asignación individual, un juego puede tirar todo el grupo para recuperar memoria.
Y en tercer lugar, los juegos siempre establecerán una especificación mínima y presupuestarán su uso de memoria dentro de eso. Entonces, si se especifica que un juego requiere 1 gb de RAM, eso significa que mientras su uso de memoria nunca exceda 1 gb, nunca tendrá que preocuparse por quedarse sin RAM.
fuente
Bueno, principalmente, de la misma manera que lo hicimos antes de que existieran excepciones: el antiguo "verificar el enfoque del valor de retorno". Si un asignador no utiliza excepciones, generalmente regresará
null
cuando falle una asignación. Un juego bien escrito verificará eso y manejará la situación de cualquier manera que sea segura. A veces, esto significa terminar el juego (p. Ej.std::terminate
. ) O al menos volver al último estado seguro conocido (p. Ej., Cuando asigna desde un grupo, puede deshacerse de todo el grupo de forma segura incluso cuando esté en un estado inseguro).Muchos juegos usan excepciones para casos como este, incluso entonces. Cuando alguien dice "evita usar excepciones en el código del juego", lo que generalmente significan es "solo usa excepciones para casos verdaderamente excepcionales, no para el control de flujo mundano". La configuración para capturar una excepción de falta de memoria es barata: el costo está principalmente en el lanzamiento y las complicaciones que se obtienen al manejar la excepción. Ninguno de los dos es muy importante en un caso típico de falta de memoria: casi siempre desea terminar de todos modos.
Eso sí, la mayoría de los juegos simplemente se bloquean. Esto no es tan loco como parece: por lo general, tienes un poco de uso de memoria como objetivo, y asegúrate de que tu juego no alcance ese límite (por ejemplo, al usar un límite de conteo de unidades en un juego de estrategia en tiempo real o al limitar el cantidad de enemigos en una sección de un FPS). Incluso si no lo hace, generalmente no se queda sin memoria en ningún sistema razonablemente moderno; por ejemplo, se queda sin espacio de direcciones virtuales (en una aplicación de 32 bits) o la pila. El sistema operativo ya finge que tienes tanta memoria como pides, esa es una de las abstracciones que proporciona la memoria virtual. El punto es que solo porque tienes 256 MiB de RAM física no significa que tu aplicación no pueda usar 4 GiB de memoria. Es posible que a algunos juegos ni siquiera les importe demasiado que todos sus datos no
fuente
Capturar una excepción y decidir qué hacer cuando se genera una excepción son dos cosas diferentes.
Debe tener una lógica compleja para liberar memoria que su programa no necesita. Peor aún, si cualquier otro programa o biblioteca en ejecución pierde la memoria, entonces no tiene control sobre ella.
Lo único bueno que puedes hacer es apagarlo con gracia y eso es lo que la mayoría de los programas elegirán hacer. Ni siquiera puede decidir esperar hasta que la memoria esté disponible porque hará que su programa no responda.
fuente
std::terminate
, y eso es todo. Pero en tales condiciones, la asignación puede devolver un puntero nulo en lugar de lanzar una excepción, y eso se puede manejar por separado.