Как механизм приложений (python) управляет памятью между запросами (Превышено мягкое ограничение частной памяти)

Я испытываю случайные Exceeded soft private memory limit ошибка в самых разных обработчиках запросов в движке приложения. Я понимаю, что эта ошибка означает, что объем оперативной памяти, используемой экземпляром, превысил выделенный объем и как это приводит к закрытию экземпляра.

Я хотел бы понять, какие могут быть возможные причины ошибки, и для начала я хотел бы понять, как ожидается, что экземпляры Python ядра приложения будут управлять памятью. Мои зачаточные предположения были:

  1. Экземпляр F2 начинается с 256 МБ
  2. Когда он запускается, он загружает код моего приложения - скажем, 30 МБ
  3. Когда он обрабатывает запрос, у него доступно 226 МБ
    • до тех пор, пока этот запрос не превышает 226 МБ (+ предел ошибки), запрос завершается без ошибки
    • если оно превышает 226 МБ + запас, экземпляр завершает запрос, регистрирует ошибку "Превышено мягкое ограничение частной памяти", затем завершается - теперь вернитесь к шагу 1
  4. Когда этот запрос возвращается, любая используемая им память освобождается, т.е. неиспользованная оперативная память возвращается к 226 МБ
  5. Шаг 3-4 повторяется для каждого запроса, переданного экземпляру, на неопределенный срок

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

а) Есть ли шаг № 4?

б) Что может привести к тому, что этого не произойдет? или не до конца случится? Например, как утечка памяти между запросами?

c) Может ли хранение в переменных уровня модуля вызвать утечку памяти? (Я сознательно не использую переменные уровня модуля таким образом)

г) Какие инструменты / методы я могу использовать, чтобы получить больше данных? Например, измерить использование памяти при входе в обработчик запросов?

В ответах / комментариях, где это возможно, просьба ссылаться на документацию gae.

[править] Дополнительная информация: мое приложение настроено как threadsafe: false, Если это имеет отношение к ответу, пожалуйста, укажите, что это такое. Я планирую изменить на threadsafe: true скоро.

[править] Пояснение: этот вопрос касается ожидаемого поведения gae для управления памятью. Так что пока предложения вроде звоните gc.collect()"вполне могут быть частичные решения связанных проблем, они не полностью отвечают на этот вопрос. Вплоть до того момента, когда я понимаю, как должен вести себя gae, используя gc.collect() было бы похоже на программирование вуду для меня.

Наконец: если я получил все это задом наперед, тогда я заранее извиняюсь - я действительно не могу найти много полезной информации об этом, поэтому я в основном догадываюсь..

3 ответа

Интерпретатор Python в App Engine не делает ничего особенного с точки зрения управления памятью по сравнению с любым другим стандартным интерпретатором Python. Так, в частности, нет ничего особенного, что происходит "на запрос", как, например, ваш гипотетический шаг 4. Скорее, как только счетчик ссылок любого объекта уменьшается до нуля, интерпретатор Python освобождает эту память (модуль gc только для того, чтобы иметь дело с мусорными циклами - когда группа объектов никогда не сводит счетчик ссылок к нулю, потому что они ссылаются друг на друга, даже если нет доступной внешней ссылки на них).

Таким образом, память может легко "просочиться" (на практике, хотя технически это не утечка) "между запросами", если вы используете какую-либо глобальную переменную - указанные переменные выживут в экземпляре класса обработчика и его (например) get метод - т. е. ваша точка зрения (с), хотя вы говорите, что не делаете этого.

Как только вы объявите свой модуль threadsafeможет случиться, что экземпляр будет обслуживать несколько запросов одновременно (до того, что вы установили как max_concurrent_requests в automatic_scaling раздел вашего модуля .yaml конфигурационный файл; значение по умолчанию 8). Таким образом, оперативная память вашего экземпляра должна быть кратна тому, что нужно каждому запросу.

Что касается (d), чтобы "получить больше данных" (я думаю, вы на самом деле имеете в виду, получить больше оперативной памяти), единственное, что вы можете сделать, это настроить больший instance_class для вашего модуля, жаждущего памяти.

Чтобы использовать меньше оперативной памяти, есть много методов - которые не имеют ничего общего с App Engine, все, что связано с Python, и, в частности, все, что связано с вашим очень конкретным кодом и его очень специфическими потребностями.

Единственная проблема GAE, о которой я могу подумать, заключается в том, что ndbБыло обнаружено, что кеширование утечки - см. https://code.google.com/p/googleappengine/issues/detail?id=9610; этот поток также предлагает обходные пути, такие как отключение ndb кэширование или переход к старому db (который не кэшируется и не имеет утечки). Если вы используете ndb и не отключили его кэширование, которое может быть основной причиной проблем с утечкой памяти, которые вы наблюдаете.

Взгляните на эти вопросы и ответы, чтобы узнать о подходах к проверке сборки мусора и о возможных альтернативных объяснениях: использование памяти для запросов к БД Google App Engine

Точка 4 - неверное предположение, сборщик мусора в Python не так легко возвращает память, программа Python забирает эту память, но не используется, пока сборщик мусора не пройдет. В то же время, если какой-то другой запрос требует больше памяти - может быть выделен новый, поверх памяти из первого запроса. Если вы хотите заставить Python собирать мусор, вы можете использовать gc.collect() как упомянуто здесь

Другие вопросы по тегам