Czy operator "nowy" w C++ może kiedykolwiek zrobić wyjątek w prawdziwym życiu?

Czy operator new może rzucić wyjątek w prawdziwym życiu?

A jeśli tak, to czy mam jakieś opcje obsługi takiego wyjątku oprócz zabicia mojej aplikacji?

Aktualizacja:

Czy jakiekolwiek prawdziwe, new-ciężkie aplikacje sprawdzają czy nie ma awarii i odzyskują je, gdy nie ma pamięci?


Zobacz też:

Author: Community, 2010-03-23

18 answers

Nowy operator i nowy operator[] powinny rzucać std::bad_alloc, ale nie zawsze tak jest, ponieważ zachowanie może być czasami nadpisane.

Można użyć std::set_new_handler i nagle może się zdarzyć coś zupełnie innego niż rzucanie std::bad_alloc. Chociaż standard wymaga, aby użytkownik albo udostępnił pamięć, przerwał lub wyrzucił std::bad_alloc. Ale oczywiście może tak nie być.

Zastrzeżenie: nie sugeruję, aby to zrobić.

 24
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 13:27:36

Tak, new może i rzuci, jeśli alokacja nie powiedzie się. Może się to zdarzyć, jeśli zabraknie pamięci lub spróbujesz przydzielić zbyt duży blok pamięci.

Możesz złapać wyjątek std::bad_alloc i odpowiednio go obsłużyć. Czasami ma to sens, innym razem (Czytaj: przez większość czasu) tak nie jest. jeśli, na przykład, próbowałeś przydzielić ogromny bufor, ale mogłeś pracować z mniejszą ilością miejsca, możesz spróbować przydzielić sukcesywnie mniejsze bloki.

 43
Author: James McNellis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:24:59

Jeśli używasz typowego wbudowanego procesora z systemem Linux bez pamięci wirtualnej, jest całkiem prawdopodobne, że twój proces zostanie zakończony przez system operacyjny, zanim nowy zawiedzie, jeśli przydzielisz zbyt dużo pamięci.

Jeśli program jest uruchamiany na komputerze z mniejszą ilością pamięci fizycznej niż maksymalna ilość pamięci wirtualnej (2 GB w standardowym systemie Windows), okaże się, że po przydzieleniu ilości pamięci w przybliżeniu równej dostępnej pamięci fizycznej, dalej przydziały się powiedzie, ale spowoduje stronicowanie na dysk. Spowoduje to zablokowanie Twojego programu i może nie być w stanie dotrzeć do punktu wyczerpania pamięci wirtualnej. Więc możesz nie dostać wyjątku wyrzucony.

Jeśli masz więcej pamięci fizycznej niż pamięci wirtualnej i po prostu zachować alokacji pamięci, otrzymasz wyjątek, gdy wyczerpane pamięci wirtualnej do punktu, w którym nie można przydzielić rozmiar bloku żądanego.

Jeśli masz długotrwały program, który przydziela i zwalnia w wielu różnych rozmiarach bloków, w tym małych bloków, o wielu różnych cyklach życia, pamięć wirtualna może zostać rozdrobniona do tego stopnia, że new nie będzie w stanie znaleźć wystarczająco dużego bloku, aby spełnić żądanie. Wtedy new rzuci wyjątek. Jeśli zdarzy ci się, że masz wyciek pamięci, który przecieka sporadycznie mały blok w losowej lokalizacji, która ostatecznie fragmentuje pamięć do punktu, w którym arbitralnie mała alokacja bloków nie powiedzie się, a wyjątek zostanie wyrzucony.

Jeśli masz błąd programu, który przypadkowo przekazuje duży rozmiar tablicy do new [], new nie powiedzie się i wyrzuci wyjątek. Może się to zdarzyć na przykład, jeśli rozmiar tablicy jest w rzeczywistości jakimś losowym wzorcem bajtów, być może pochodzącym z niezainicjalizowanej pamięci lub uszkodzonego strumienia komunikacyjnego.

Wszystko to dotyczy domyślnej globalnej nowej. Możesz jednak zastąpić nową globalną i podać nową specyficzną dla danej klasy. Te też mogą rzucać, a znaczenie tej sytuacji zależy od tego, jak ją zaprogramowałeś. Zwykle dla new dołącza się pętlę, która próbuje wszystkich możliwych sposobów uzyskania żądanej pamięci. Rzuca, gdy wszystkie są wyczerpane. To, co wtedy zrobisz, zależy od Ciebie.

Możesz wyłapać wyjątek z new i skorzystać z możliwości, jaką daje, aby udokumentować stan programu w czasie wystąpienia wyjątku. Możesz "zrzucić rdzeń". Jeśli przy uruchamianiu programu masz przydzielony okrągły bufor oprzyrządowania, możesz go zrzucić na dysk przed zakończeniem programu. Zakończenie programu może być wdzięczne, co jest przewagą nad zwyczajnym nie obchodzeniem się z wyjątkiem.

Osobiście nie widziałem przykładu, gdzie można uzyskać dodatkową pamięć po wyjątku. Jedna z możliwości jest jednak następująca: Załóżmy, że masz alokator pamięci, który jest wysoce wydajny, ale nie jest dobry w odzyskiwaniu wolnej przestrzeni. Na przykład, może być podatny na fragmentację wolnej przestrzeni, w której wolne bloki sąsiadują, ale Nie połączone. Możesz użyć wyjątku od new, złapanego w new_handler, aby uruchomić procedurę zagęszczania wolnego miejsca przed ponowną próbą.

Poważne programy powinny traktować pamięć jako potencjalnie deficytowy zasób, kontrolować jej alokację tak bardzo, jak to możliwe, monitorować jej dostępność i odpowiednio reagować, jeśli coś wydaje się pójść dramatycznie źle. Na przykład, można zrobić przypadek, że w każdym prawdziwym programie jest dość mała górna granica parametru rozmiar przekazanego do alokator pamięci, a wszystko większe od tego powinno powodować jakiś rodzaj obsługi błędów, niezależnie od tego, czy żądanie może być spełnione. Można argumentować, że tempo wzrostu pamięci długotrwałego programu powinno być monitorowane, a jeśli można rozsądnie przewidzieć, że program wyczerpie dostępną pamięć w najbliższej przyszłości, należy rozpocząć uporządkowany restart procesu.

 19
Author: Permaquid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-02 04:54:23

Osgx powiedział:

Czy jakiekolwiek aplikacje w świecie rzeczywistym sprawdza wiele wiadomości i może odzyskać, gdy nie ma pamięci?

Odpowiedziałem na to wcześniej w moja odpowiedź na to pytanie , które jest cytowane poniżej:

Bardzo trudno sobie z tym poradzić taka sytuacja. Możesz chcieć zwrócenie użytkownikowi znaczącego błędu swojej aplikacji, ale jeśli jest to problem spowodowany brakiem pamięci, ty może nie nawet być w stanie sobie pozwolić na pamięć do przydzielenia Komunikatu o błędzie. To trochę zła sytuacja-22 naprawdę.

Istnieje defensywne programowanie technika (czasami nazywana pamięcią spadochron lub deszczowy dzień funduszu), gdzie można przydzielić kawałek pamięci, gdy twój uruchomienie aplikacji. When you then obsłuż wyjątek bad_alloc, ty uwolnij tę pamięć i użyj dostępna pamięć do zamknięcia zastosowanie: wyświetlanie znaczącego błędu do na użytkownik. To jest o wiele lepsze niż upaść :)

 9
Author: LeopardSkinPillBoxHat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:17

W systemach uniksowych zwyczajem jest uruchamianie długotrwałych procesów z limitami pamięci (używając ulimit), aby nie pożerać całej pamięci systemu. Jeśli twój program przekroczy ten limit, otrzymasz std::bad_alloc.


Update for OP ' s edit: najbardziej typowym przypadkiem programów odzyskujących stan poza pamięcią jest w systemach zbieranych śmieci, które następnie wykonują GC i kontynuują. Chociaż ten rodzaj GC na żądanie jest naprawdę przeznaczony tylko do ostatnich wysiłków; zazwyczaj dobre programy starają się GC okresowo w celu zmniejszenia naprężeń na kolektor.

W przypadku programów innych niż GC odzyskiwanie po problemach z brakiem pamięci jest mniej powszechne, ale w przypadku serwerów internetowych jednym ze sposobów odzyskania jest odrzucenie żądania, które powoduje, że pamięć wyczerpuje się z "tymczasowym" błędem. (Strategia" kto pierwszy, ten lepszy".)

 9
Author: Chris Jester-Young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 13:26:06

Nie musisz obsługiwać WYJĄTKÓW w każdym new:) wyjątki mogą się rozmnażać. Zaprojektuj swój kod tak, aby w każdym "module" były określone punkty, w których ten błąd jest obsługiwany.

 7
Author: moogs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:39:59

To zależy od kompilatora / środowiska uruchomieniowego i od operator new, którego używasz (np. niektóre wersje Visual Studio nie wyrzucą z pudełka, ale zamiast tego zwrócą wskaźnik NULL A la malloc.)

Zawsze możesz catch A std::bad_alloc wyjątek, lub jawnie użyć nothrow new aby powrócić NULL zamiast rzucać. (Zobacz też ostatnie posty StackOverflow obracające się wokół tematu.)

Zauważ, że operator new, jak malloc, will fail when 2-3GB w 32-bitowym procesie w zależności od systemu operacyjnego), poza limitem (wspomniano już oulimit) lub poza ciągłą przestrzenią adresową (np. fragmented heap).)

 7
Author: vladr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:09:59

Tak, new możesz rzucić std::bad_alloc (podklasa std::exception), którą możesz złapać.

Jeśli chcesz uniknąć tego wyjątku, a zamiast tego jesteś gotowy do przetestowania wyniku new dla wskaźnika null, możesz dodać nothrow argument:

T* p = new (nothrow) T(...);
if (p == 0)
{
    // Do something about the bad allocation!
}
else
{
    // Here you may use p.
}
 4
Author: squelart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:34:57

Yes new rzuci wyjątek, jeśli nie ma więcej dostępnej pamięci, ale to nie znaczy, że powinieneś zawinąć każdą nową w try ... catch. Złap wyjątek tylko wtedy, gdy twój program może coś z tym zrobić.

Jeśli program nie może nic zrobić, aby poradzić sobie z tą wyjątkową sytuacją, co często zdarza się, gdy zabraknie pamięci, nie ma sensu łapać wyjątku. Jeśli jedyną rzeczą, którą możesz rozsądnie zrobić, to przerwać program, Możesz również pozwolić na wyjątek bubble do najwyższego poziomu, gdzie zakończy program, jak również.

 4
Author: sth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:41:29

W wielu przypadkach nie ma racjonalnego odzyskiwania w sytuacji braku pamięci, w którym to przypadku prawdopodobnie jest całkowicie rozsądne, aby pozwolić aplikacji zakończyć. Możesz chcieć złapać wyjątek na wysokim poziomie, aby wyświetlić ładniejszy komunikat o błędzie niż domyślnie podany przez kompilator, ale być może będziesz musiał wykonać kilka sztuczek, aby to nawet zadziałało(ponieważ proces może być bardzo mało zasobów w tym momencie).

Chyba, że masz specjalną sytuację, która może być obsługiwane i odzyskane, prawdopodobnie nie ma powodu, aby poświęcać wiele wysiłku, próbując poradzić sobie z wyjątkiem.

 3
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 04:05:17

Zauważ, że w Windows, bardzo duże nowe / mallocs będą po prostu przydzielać z pamięci wirtualnej. W praktyce Twój komputer ulegnie awarii, zanim zobaczysz ten wyjątek.

char *pCrashMyMachine = new char[TWO_GIGABYTES];
Spróbuj, jeśli się odważysz!
 3
Author: Erik Hermansen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-24 02:17:50

Używam Mac OS X i nigdy nie widziałem malloc return NULL (co oznaczałoby wyjątek od new W C++). Maszyna kończy pracę, dokłada wszelkich starań, aby przydzielić zmniejszającą się pamięć procesom, a na koniec wysyła SIGSTOP i zaprasza użytkownika do zabijania procesów, a nie do radzenia sobie z awarią alokacji.

Znajduje się tu jednak tylko jeden peron. Z pewnością istnieją platformy, na których domyślny alokator rzuca. I, jak mówi Chris, ulimit może wprowadzić sztuczne ograniczenie tak że wyjątkiem byłoby oczekiwane zachowanie.

Poza domyślnym/malloc istnieją również alokatory. Jeśli Klasa nadpisuje operator new, używasz niestandardowych argumentów do new(…) lub przekazujesz obiekt alokatora do kontenera, prawdopodobnie definiuje on własne warunki do wyrzucenia bad_alloc.

 2
Author: Potatoswatter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:40:54

Nowy operator wyrzuci wyjątek std:: bad_alloc, gdy w Puli nie ma wystarczającej ilości dostępnej pamięci do spełnienia żądania runtime.

Może się to zdarzyć przy złym projekcie lub gdy pamięć przydzielona nie jest zwalniana poprawnie.

Obsługa takiego wyjątku opiera się na Twoim projekcie, jednym ze sposobów będzie pauza i ponowna próba jakiś czas później, mając nadzieję, że więcej pamięci wróci do puli i żądanie może się udać.

 1
Author: YeenFei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:32:15

Najbardziej realistycznie Nowy będzie rzucać ze względu na decyzję o ograniczeniu zasobu. Powiedzmy, że ta klasa (która może być intensywna w pamięci) pobiera pamięć z puli fizykalnej i jeśli wiele obiektów z niej bierze (potrzebujemy pamięci dla innych rzeczy, takich jak dźwięk, tekstury itp.), może rzucić zamiast upaść później, gdy coś, co powinno być w stanie przydzielić pamięć, zabiera ją. (wygląda na dziwny efekt uboczny).

Przeciążenie new może być przydatne w urządzeniach z ograniczoną pamięcią. Takich jak palmtopy lub na konsolach, gdy jest zbyt łatwo przejść za burtę z fajnymi efektami.

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 03:00:12

Yes, new can and will throw.

Ponieważ pytasz o "prawdziwe" programy: pracowałem nad różnymi komercyjnymi aplikacjami opakowanymi w folię termokurczliwą od ponad 20 lat. "Prawdziwe" programy z milionami użytkowników. Które możesz kupić z półki już dziś. Tak, Nowy może rzucać.

Są różne sposoby, aby sobie z tym poradzić.

Najpierw napisz swój własny new_handler(jest to wywoływane przed new gives up i throws-patrz funkcja set_new_handler ()). Gdy twój new_handler jest zadzwoniłem, może uda Ci się uwolnić rzeczy, których naprawdę nie potrzebujesz. Ostrzegaj również użytkownika, że ma mało pamięci. (tak, może być trudno ostrzec użytkownika o niczym, jeśli jesteś naprawdę niski).

Jedną rzeczą jest posiadanie wstępnie przydzielonej, przy uruchomieniu Twojego programu trochę 'dodatkowej' pamięci. Gdy zabraknie pamięci, użyj tej dodatkowej pamięci, aby pomóc zapisać kopię dokumentu użytkownika na dysku. Następnie ostrzec, i może wyjść z wdziękiem.

Itd. To tylko przegląd, oczywiście jest w tym coś więcej.

Obsługa niskiej pamięci nie jest łatwa.

 1
Author: tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 04:19:43

Funkcja new-handler jest funkcją wywoływaną przez funkcje alokacji, gdy nowa próba alokacji pamięci nie powiedzie się. Możemy mieć własne logowanie lub jakąś akcję specjalną, np. Jego przeznaczenie jest jedną z trzech rzeczy: 1) Udostępnij więcej pamięci 2) zakończenie programu (np. przez wywołanie STD:: terminate) 3) wyrzuć wyjątek typu std:: bad_alloc lub pochodzący od std::bad_alloc. Domyślna implementacja wyrzuca STD:: bad_alloc. Użytkownik może mieć swój własny nowy-handler, który może oferować zachowanie inne niż domyślne. To powinno być używane tylko wtedy, gdy naprawdę potrzebujesz. Zobacz przykład, aby uzyskać więcej wyjaśnień i domyślnego zachowania,

#include <iostream>
#include <new>

void handler()
{
    std::cout << "Memory allocation failed, terminating\n";
    std::set_new_handler(nullptr);
}

int main()
{
    std::set_new_handler(handler);
    try {
        while (true) {
            new int[100000000ul];
        }
    } catch (const std::bad_alloc& e) {
        std::cout << e.what() << '\n';
    }
}
 1
Author: Bhupesh Pant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-02 13:01:25

Dobrze jest sprawdzić / złapać ten wyjątek, gdy przydzielasz pamięć na podstawie czegoś podanego z zewnątrz( z przestrzeni użytkownika, np. sieci), ponieważ może to oznaczać próbę skompromitowania Twojej aplikacji / usługi / systemu i nie powinieneś na to pozwolić.

 1
Author: zoska,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-02 13:05:20

new operator wyrzuci wyjątek std::bad_alloc, gdy zabraknie pamięci (dokładniej pamięci wirtualnej).

Jeśli new rzuca wyjątek, to jest to poważny błąd:

  • więcej niż dostępna maszyna wirtualna zostaje przydzielona (ostatecznie się nie powiedzie). Możesz spróbować zmniejszyć ilość pamięci niż wyjść z Programu poprzez wyłapanie wyjątku std::bad_alloc.
 0
Author: aJ.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-23 02:26:45