У меня есть приложение Django, которое демонстрирует странное поведение коллекции мусора. Существует, в частности, один вид, который будет просто постоянно увеличивать размер виртуальной машины каждый раз, когда он будет вызван, - до определенного предела, и в этом случае использование снова падает. Проблема в том, что до достижения этой точки требуется значительное время, и на самом деле виртуальная машина, на которой работает мое приложение, не имеет достаточной памяти для всех процессов FCGI, которые занимают столько же памяти, сколько они иногда делают.Python: поведение сборщика мусора
Я провел последние два дня, исследуя это и узнав о сборке мусора Python, и я думаю, что понимаю, что происходит сейчас - по большей части. При использовании
gc.set_debug(gc.DEBUG_STATS)
Тогда для одного запроса, я вижу следующий вывод:
>>> c = django.test.Client()
>>> c.get('/the/view/')
gc: collecting generation 0...
gc: objects in each generation: 724 5748 147341
gc: done.
gc: collecting generation 0...
gc: objects in each generation: 731 6460 147341
gc: done.
[...more of the same...]
gc: collecting generation 1...
gc: objects in each generation: 718 8577 147341
gc: done.
gc: collecting generation 0...
gc: objects in each generation: 714 0 156614
gc: done.
[...more of the same...]
gc: collecting generation 0...
gc: objects in each generation: 715 5578 156612
gc: done.
Таким образом, по существу, огромное количество объектов выделяются, но первоначально перемещены в поколение 1, и когда ген 1 подбирается во время одного и того же запроса, они перемещаются в поколение 2. Если после этого я сделаю ручной gc.collect (2), они будут удалены. И, как я уже упоминал, там также удаляется, когда происходит следующая автоматическая развертка gen 2, которая, если я правильно понимаю, в этом случае будет примерно как каждые 10 запросов (на данный момент приложение требует около 150 МБ).
Хорошо, поэтому изначально я думал, что в обработке одного запроса может быть циклическая ссылка, которая предотвращает сбор любого из этих объектов при обработке этого запроса. Тем не менее, я потратил часы, пытаясь найти один, используя pympler.muppy и objgraph, как после, так и после отладки внутри обработки запроса, и, похоже, нет. Скорее, кажется, что 14 000 объектов или так, которые были созданы во время запроса, все находятся в цепочке ссылок на некоторый глобальный объект-запрос, то есть как только запрос уходит, их можно освободить.
Это была моя попытка объяснить это, так или иначе. Однако, если это так, и на самом деле нет зависимостей на велосипеде, не следует освобождать все дерево объектов, как только объект запроса, который заставляет их удерживаться, уходит, без участия сборщика мусора, чисто благодаря подсчетам ссылок падение до нуля?
С этой установкой, вот мои вопросы:
ли выше, даже имеет смысл, или я должен искать проблему в другом месте? Неужели это несчастный случай, что значимые данные хранятся так долго в этом конкретном случае использования?
Есть ли что-нибудь, что я могу сделать, чтобы избежать проблемы. Я уже вижу некоторый потенциал для оптимизации представления, но это похоже на решение с ограниченным охватом - хотя я не уверен, что я тоже был бы общим; как это целесообразно, например, для вызова gc.collect() или gc.set_threshold() вручную?
С точки зрения того, как сам сборщик мусора работает:
Правильно ли я понимаю, что объект всегда перемещается к следующему поколению, если зачистка смотрит на него, и определяет, что ссылки это имеет , а не циклический, но на самом деле может быть отнесен к корневому объекту.
Что произойдет, если gc выполнит, скажем, прогон 1 поколения, и найдет объект, на который ссылается объект в генерации 2; следует ли следовать этой взаимосвязи внутри поколения 2 или ждать, пока произойдет пробой поколения 2, прежде чем анализировать ситуацию?
При использовании gc.DEBUG_STATS я в первую очередь забочусь о информации об объектах в каждом поколении; однако я продолжаю получать сотни «gc: 0.0740s истек». «gc: 1258233035.9370s истек». Сообщения; они совершенно неудобны - для их распечатки требуется значительное время, и они затрудняют поиск интересных вещей. Есть ли способ избавиться от них?
Я не думаю, что есть способ сделать gc.get_objects() по генерации, т. Е. Только получить объекты из поколения 2, например?
+1 для упоминания установки DEBUG = False, поэтому Django не регистрирует все ваши SQL-запросы. – Kekoa
Вау, какой ментальный образ Django есть много еды –