Jak gry C ++ radzą sobie z błędami alokacji pamięci?


23

Znam kilka gier napisanych w C ++, ale nie używających wyjątków. Ponieważ obsługa awarii alokacji pamięci w C ++ jest generalnie oparta na std::bad_allocwyjątku, w jaki sposób te gry radzą sobie z taką awarią?

Czy po prostu ulegają awarii, czy istnieje inny sposób radzenia sobie z błędem braku pamięci?


1
Należy pamiętać, że istnieją pewne środowiska / systemy operacyjne, w których alokacja jest udana, ale próba użycia pamięci powoduje awarię programu (lub innego) programu
PlasmaHH

6
Jeśli alokacja nie powiedzie się podczas gry, Quake 3 spowoduje powrót do menu z komunikatem o błędzie. Wierzę, że jest to możliwe dzięki alokatorowi pul Quake 3, który może po prostu upuścić całą pulę i bezpiecznie wrócić do menu, jeśli pula się skończy.
Dietrich Epp,

16
Zwykle przez awarię.
user253751,

1
Jeśli wyjątki są rzeczywiście wyłączone, program musi zamiast tego wywołać std::terminatei to wszystko. Ale następnie pod tym samym ograniczeniem alokacja może zwrócić wskaźnik zerowy zamiast zgłaszania wyjątku, a wynik ten można sprawdzić i obsłużyć osobno.
underscore_d

2
W C ++ możesz powiedzieć ClassName variableName = new(nothrow) ClassName();( oczywiście zastępując nazwę klasy, nazwę zmiennej itp. ). Następnie, jeśli alokacja nie powiedzie się, możesz to wykryć, mówiąc, if(!variableName)że można obsłużyć błąd bez bloku wyjątku try-catch. Podobnie, jeżeli pamięć jest przeznaczona użyciu funkcji takich jak malloc(), calloc()itp, a następnie awarii przydzielić można wykryć stosując ten sam if(!variableName)sposób bez potrzeby try-haczyk. Jeśli chodzi o sposób, w jaki gry radzą sobie z tymi błędami, to w tym momencie twórcy gry decydują, czy ulegnie awarii, czy nie.
Spencer D

Odpowiedzi:


63

Tak samo jak wszystkie przeciętne programy: nie. *

W przypadku większości aplikacji nie oczekuje się od klientów kontynuowania pracy po wyczerpaniu pamięci. Wszystkie gry należą do tych „większości aplikacji”. Nie ma sensu poświęcanie czasu i pieniędzy na pracę przy wyjątkowej walizce, której klient nie oczekuje od pracy.

Pytanie jest podobne do następującego:

  • Jak zainstalować grę, jeśli dysk twardy jest pełny?
  • Jak nadal uruchamiasz grę z dużą liczbą klatek na sekundę na PC poniżej minimalnych specyfikacji?

Odpowiedź jest taka sama: są to problemy użytkowników. Ta gra nie obchodzi.


* W rzeczywistości zazwyczaj przechwytują wyjątek na wysokim poziomie i wykorzystują pamięć, która została wstępnie przydzielona na początku gry, aby spróbować zarejestrować zdarzenie przed zawieszeniem / zakończeniem. Następnie dziennik pozwala obsłudze klienta tracić mniej czasu na problem.


2
W sprawie
wstępnego

23

Zazwyczaj tego rodzaju scenariusz nigdy się nie zdarza.

Po pierwsze, pamięć wirtualna we współczesnych systemach operacyjnych oznacza, że ​​i tak jest mało prawdopodobne w normalnej pracy; chyba że wystąpi błąd niekontrolowanej alokacji, gra będzie alokowała pamięć z wirtualnej przestrzeni adresowej systemu operacyjnego, a system operacyjny będzie sprawdzał wprowadzanie i wyłączanie stronicowania.

To wszystko dobrze i dobrze, ale tak naprawdę nie dotyczy to konsol ani starszych systemów operacyjnych, więc po drugie, gry nawet nie wykonują wielu małych dynamicznych alokacji przy użyciu standardowych alokatorów bibliotecznych. Zamiast tego będą jednorazowo przydzielać dużą pulę pamięci podczas uruchamiania i pobierać z tej puli wszelkie wymagane alokacje środowiska wykonawczego (np. Używając nowego umieszczenia C ++ lub pisząc własny system zarządzania pamięcią na na początku).

To także chroni przed wyciekiem pamięci, ponieważ zamiast śledzić i rozliczać każde małe indywidualne przydziały, gra może po prostu wyrzucić całą pulę, aby odzyskać pamięć.

I po trzecie, gry zawsze ustalają minimalną specyfikację i w ramach tego budżetują zużycie pamięci. Jeśli więc gra wymaga 1 GB pamięci RAM, oznacza to, że dopóki jej użycie pamięci nigdy nie przekroczy 1 GB, nigdy nie musi się martwić, że zabraknie pamięci RAM.


3
„Zamiast tego będą jednorazowo przydzielać dużą pulę pamięci przy starcie i pobierać z tej puli wszelkie wymagane alokacje środowiska wykonawczego” - a jak myślisz, co się stanie, jeśli pula będzie pełna?
user253751,

@immibis - proszę zobaczyć mój trzeci punkt.
Maximus Minimus

2
@immibis Masz na myśli ... co robią, jeśli pula jest pusta? W przeciwieństwie do pamięci systemowej, wielkość i wykorzystanie puli jest całkowicie pod kontrolą aplikacji. Dlatego pula nie może stać się pusta, chyba że aplikacja ma błąd.
David Schwartz,

@DavidSchwartz Lub chyba, że ​​jądro korzysta z nadmiernej pamięci. Linux to robi. Nawet wyzerowanie puli nie pomaga, jeśli kompresja pliku strony jest włączona przez zswap lub zram.
Damian Yerrick

1
To nie wydaje się odpowiadać na rzeczywiste pytanie, ale stwierdza coś podobnego do „Ten statek jest niezatapialny, więc do czego potrzebowalibyśmy procedury awaryjnej?”
Lilienthal,

2

Cóż, głównie w ten sam sposób, w jaki to zrobiliśmy, zanim istniały wyjątki - stare „sprawdź podejście do wartości zwrotu”. Jeśli alokator nie korzysta z wyjątków, zwykle powraca, nullgdy alokacja się nie powiedzie. Sprawdzona gra sprawdzi to i poradzi sobie z sytuacją w bezpieczny sposób. Czasami oznacza to zakończenie gry (np. std::terminate) Lub przynajmniej powrót do ostatniego znanego bezpiecznego stanu (np. Kiedy przydzielisz z puli, możesz bezpiecznie pozbyć się całej puli, nawet gdy jest ona w niebezpiecznym stanie).

Wiele gier korzysta nawet z wyjątków w takich przypadkach. Kiedy ktoś mówi „unikaj stosowania wyjątków w kodzie gry”, zwykle oznacza to „używaj wyjątków tylko w naprawdę wyjątkowych przypadkach, a nie w przypadku zwykłej kontroli przepływu”. Konfiguracja wychwytywania wyjątku braku pamięci jest tania - koszty są głównie związane z tym problemem i komplikacjami związanymi z obsługą wyjątku. Żadne z nich nie jest bardzo ważne w typowym przypadku braku pamięci - prawie zawsze i tak chcesz zakończyć.

Pamiętaj, że większość gier ulegnie awarii. To nie jest tak szalone, jak się wydaje - zwykle używasz pamięci jako celu i upewnij się, że twoja gra nie osiąga tego limitu (na przykład, stosując limit liczby jednostek w grze RTS lub ograniczając ilość wrogów w sekcji FPS). Nawet jeśli tego nie zrobisz, zwykle nie brakuje pamięci w żadnym stosunkowo nowoczesnym systemie - na przykład brakuje wirtualnej przestrzeni adresowej (w aplikacji 32-bitowej) lub stosu. System operacyjny już udaje, że masz tyle pamięci, ile prosisz - to jedna z abstrakcji, jaką zapewnia pamięć wirtualna. Chodzi o to, że to, że masz 256 MiB fizycznej pamięci RAM, nie oznacza, że ​​twoja aplikacja nie może użyć 4 GiB pamięci. Niektóre gry mogą nawet nie mieć nic przeciwko temu, że wszystkie ich dane nie są


1

Łapanie wyjątku i podejmowanie decyzji, co zrobić, kiedy zostanie zgłoszony wyjątek, to dwie różne rzeczy.

Musisz mieć złożoną logikę, aby zwolnić pamięć, która nie jest potrzebna Twojemu programowi. Co gorsza, jeśli jakiś inny uruchomiony program lub biblioteka wycieka z pamięci, nie masz nad nią kontroli

Jedyną dobrą rzeczą, którą możesz zrobić, aby zamknąć się z wdziękiem i to właśnie zdecyduje większość programów. Nie możesz nawet zdecydować się poczekać, aż pamięć będzie dostępna, ponieważ spowoduje to brak reakcji Twojego programu.


Jeśli omawiane gry dosłownie „nie używają wyjątków”, jak powiedział PO, to nie mogą złapać, przebić ani przetworzyć jednej. Jeśli wyjątki są wyłączone, a ktoś je zgłasza, program musi zamiast tego wywołać std::terminatei to wszystko. Ale w takich warunkach alokacja może zwrócić wskaźnik zerowy zamiast zgłaszania wyjątku i może być obsługiwana osobno.
underscore_d
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.