Подтвердить что ты не робот

Понять поведение нового обработчика

Я читаю "Эффективный С++ 55" Скотта Мейерса и задаю вопрос из пункта 49:

Когда operator new не может выполнить запрос памяти, он вызывает new-handler, пока не найдет достаточно памяти.

Хорошо спроектированная функция newhandler должна выполнить одно из следующих действий:

  • Сделайте больше доступной памяти.
  • Установите другой новый обработчик.
  • Удалите новый обработчик
  • Выбросить исключение
  • Не возвращать

Когда new не может выделить память, это означает, что памяти недостаточно, и вопрос в том, как и откуда может новый дескриптор выделить больше памяти?

Можете ли вы объяснить все эти шаги?

4b9b3361

Ответ 1

Это зависит от реализации. Я могу сказать вам, как я обычно это делаю:

1) Новый обработчик выделяет большой объем памяти при запуске в качестве резерва.

2) Когда обычные распределения не выполняются, новый обработчик опускается в резерв.

3) Код, который контролирует управление нагрузкой, может подключить систему управления памятью и определить, когда она погрузилась в ее резерв. Он обычно реагирует путем обрезки кешей и потери нагрузки.

4) Менеджер памяти пытается пополнить свои резервы по мере освобождения памяти.

5) Когда резерв восстанавливается, крючки уведомляются о том, что они могут увеличивать кеши и/или возобновлять прием дополнительной нагрузки.

6) Когда запасы становятся низкими, выходы, которые могут выйти из строя (обычно большие распределения), терпят неудачу. Весь код должен корректно обрабатывать отказ больших распределений.

7) Если запасы исчерпаны, блокировки, которые не могут сбой (как правило, небольшие распределения), блокируются.

8) Если условие блокировки сохраняется или большие распределения продолжают терпеть неудачу, и резерв не может быть восстановлен, запускается аномальное завершение.

Ответ 2

Он может отбросить данные, которые действительно не нужны. Скажем, фотошоп кэширует отображаемое изображение в нескольких масштабах и сохраняет как можно больше. Вот как он знает, насколько это может сойти с рук.

Ответ 3

Не вернуть

Он просто попробовал все, что мог, и не смог сделать больше доступной памяти. Он вызывает exit() или abort() и завершает процесс.

Это можно использовать как стратегию с быстрым отказом. Если память низкая в системе, и известно, что некоторые процессы не требуются (например, работники-дети), вызывающие abort(), освободят память для других процессов и это самый быстрый способ избавиться от процессов.

выдать исключение

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

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

Удалите новый обработчик

Глядя на книгу, вызывающую set_new_handler(nullptr);, следует удалить новый обработчик. По словам Скотта Мейера, это должно привести к operator new выбросу исключения bad_alloc. Это должно быть идентично самому исключению исключения.

Установите новый новый обработчик

Так как вызов new_handler вызывается до тех пор, пока не будет разрешена ошибка распределения (то есть: он не разрешен operator new), если предоставленная реализация new_handler не может решить проблему, она должна распространять условие на другой слой.

Если бы я сам установил new_handler, я бы сохранил ссылку (см. также: get_new_handler()) в предыдущий new_handler, чтобы поменять его обратно, когда мой алгоритм не работает.

Сделать доступной память

Предполагается, что приложение установило new_handler. Автор new_handler имеет здесь возможность исследовать или модифицировать выделенную память. Поэтому программист установил new_handler, потому что он знает, что есть свободная память. В случае, если все пойдет не так, существует стратегия восстановления после этого сбоя.

Неполный список того, что может происходить (частичное угадывание):

  • ваша операционная система позволяет вам вручную вызвать убийцу из памяти (чтобы освободить память)
  • Если вы используете сборщик мусора Boehm, вы можете называть GC_gcollect(); для принудительной сборки мусора.
  • Вы сами зарезервировали какую-то память, которую вы сейчас используете.
  • вы вручную вызываете delete для известных расходуемых неважных объектов (кешей).
  • У вас есть интерпретатор, работающий как Perl, Python или Mono и вызывающий его сборщик мусора, чтобы сказать им, что вы находитесь в состоянии низкой памяти (или они сами устанавливают новый обработчик в своих подпрограммах init()).

Реализация

Вот реализация libstdС++.