2009-02-21 10 views
3

GAE имеет различные ограничения, один из которых представляет собой размер наибольшего выделяемого блока памяти в размере 1 Мб (теперь в 10 раз больше, но это не меняет вопрос). Ограничение означает, что в список() нельзя поместить больше некоторого количества элементов, поскольку CPython попытается выделить смежный блок памяти для указателей элементов. Наличие огромного списка() s можно считать плохой практикой программирования, но даже если в самой программе не создается огромная структура, CPython поддерживает некоторые за кулисами.Внутренние структуры CPython

Похоже, что CPython поддерживает один глобальный список объектов или что-то в этом роде. То есть приложение с множеством мелких объектов имеет тенденцию выделять все большие и большие отдельные блоки памяти.

Первой идеей был gc, и ее отключение изменяет поведение приложения немного, но все же сохраняются некоторые структуры.

Простейшие короткого приложение, которое испытывают проблемы является:

a = b = [] 
number_of_lists = 8000000 
for i in xrange(number_of_lists): 
    b.append([]) 
    b = b[0] 

Может кто-нибудь просветить меня, как предотвратить CPython от выделения огромных внутренних структур при наличии многих объектов в приложении?

+0

Что такое ** настоящий ** вопрос. Почему заканчивается память? Какая конкретная проблема у вас есть, которая, кажется, требует огромных блоков памяти? Вероятно, вы можете исправить эту проблему, чтобы не выделять память в первую очередь. Какую ** настоящую ** проблему вы пытаетесь решить? –

ответ

8

В 32-разрядной системе каждый из созданных вами 8000000 будет выделять 20 байтов для самого объекта списка, плюс 16 байтов для вектора элементов списка. Таким образом, вы пытаетесь выделить не менее (20 + 16) * 8000000 = 20168000000 байт, около 20 ГБ. И это в лучшем случае, если система malloc выделяет столько же памяти, сколько и запрошено.

Я подсчитал размер объекта списка следующим образом:

  • 2 Указатели в самой PyListObject структуре (см listobject.h)
  • 1 Указатель и один Py_ssize_t для PyObject_HEAD части объекта списка (см object.h)
  • один Py_ssize_t для PyObject_VAR_HEAD (также в object.h)

Вектор элементов списка слегка заложен, чтобы избежать необходимости изменять его размер при каждом добавлении - см. List_resize в listobject.c. Размеры: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... Таким образом, ваши списки из одного элемента будут выделять место для 4 элементов.

Ваша структура данных является несколько патологическим примером, платя цену объекта списка переменного размера без его использования - все ваши списки имеют только один элемент. Вы можете избежать обхода 12 байтов, используя кортежи вместо списков, но для дальнейшего сокращения потребления памяти вам придется использовать другую структуру данных, которая использует меньше объектов. Трудно быть более конкретным, поскольку я не знаю, чего вы пытаетесь достичь.

0

Я немного смущен относительно того, что вы просите. В этом примере кода ничего не нужно собирать мусор, так как вы никогда не убиваете никаких ссылок. Вы держите ссылку на список верхнего уровня в a, и вы добавляете вложенные списки (содержащиеся в b на каждой итерации) внутри этого. Если вы удалите 'a =', , то у вас есть объекты без ссылок.

Редактировать: В ответ на первую часть, да, Python содержит список объектов, чтобы он мог знать, что отбирать. Это весь вопрос? Если нет, прокомментируйте/отредактируйте свой вопрос, и я сделаю все возможное, чтобы помочь заполнить пробелы.

+0

Я поставил некоторый фон для выяснения. Это помогает? – myroslav

0

Что вы пытаетесь достичь с

b = b[0] 

заявления о

a = b = [] 

и

? Конечно, странно видеть такие утверждения, как в Python, потому что они не делают то, что вы можете наивно ожидать: в этом примере a и b являются двумя именами для того же списка (думайте, указатели на C). Если вы так много манипулируете, легко путать сборщик мусора (и себя!), Потому что у вас много странных ссылок, плавающих вокруг, которые не были правильно очищены.

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

+0

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

+0

Да, это просто странный пример. a = b = [] достаточно легко распутать (две ссылки на один список, который начинается пустым), но b = b [0] придает моему разуму смысл думать о внутренних компонентах CPython. Возможно, я должен просто заснуть уже ... – kquinn

+0

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

0

Чтобы вы знали об этом, у Python есть свой распределитель. Вы можете отключить его с помощью --without-pyalloc во время шага настройки.

Однако самая большая арена - 256 КБ, так что это не должно быть проблемой. Вы также можете скомпилировать Python с включенной отладкой, используя --with-pydebug. Это даст вам больше информации об использовании памяти.

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