Огромная утечка памяти при повторных вызовах os.path.isdir?
Я пишу скрипт, который связан с сканированием каталогов, и заметил серьезную утечку памяти при вызове os.path.isdir, поэтому я пробовал следующий фрагмент:
def func():
if not os.path.isdir('D:\Downloads'):
return False
while True:
func()
В течение нескольких секунд процесс Python достиг 100 МБ ОЗУ.
Я пытаюсь понять, что происходит. Похоже, что огромная утечка памяти действует только тогда, когда путь действительно является допустимым пути к каталогу (это означает, что "return False" не выполняется).
Кроме того, интересно посмотреть, что происходит в связанных вызовах, например os.path.isfile.
Мысли?
Edit:
Я думаю, что на что-то.
Хотя isfile и isdir реализованы в модуле genericpath, в системе Windows - isdir импортируется из встроенного nt.
Поэтому мне пришлось загрузить источник 2.7.3 (который я должен был сделать давно...).
После небольшого поиска я обнаружил функцию posix__isdir в \Modules\posixmodule.c, которая, как я полагаю, является функцией isdir, импортированной из nt.
Эта часть функции (и комментариев) привлекла мое внимание:
if (PyArg_ParseTuple(args, "U|:_isdir", &po)) {
Py_UNICODE *wpath = PyUnicode_AS_UNICODE(po);
attributes = GetFileAttributesW(wpath);
if (attributes == INVALID_FILE_ATTRIBUTES)
Py_RETURN_FALSE;
goto check;
}
/* Drop the argument parsing error as narrow strings
are also valid. */
PyErr_Clear();
Кажется, что все это сводится к ошибкам обработки Unicode/ASCII.
Я только что попробовал свой фрагмент выше с аргументом пути в unicode (т.е. u'D:\Downloads)) - никакой утечки памяти вообще нет. ха-ха.
Ответы
Ответ 1
Основной причиной является отказ вызова PyMem_Free
в переменной path
в пути, отличном от Unicode:
if (!PyArg_ParseTuple(args, "et:_isdir",
Py_FileSystemDefaultEncoding, &path))
return NULL;
attributes = GetFileAttributesA(path);
if (attributes == INVALID_FILE_ATTRIBUTES)
Py_RETURN_FALSE;
check:
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
В соответствии с документацией на PyArg_ParseTuple
:
-
et
: То же, что es
... -
es
: PyArg_ParseTuple()
выделяет буфер необходимого размера, копирует закодированные данные в этот буфер и корректирует * буфер для ссылки на вновь выделенное хранилище. Вызывающий отвечает за вызов PyMem_Free()
для освобождения выделенного буфера после использования.
Это ошибка в стандартной библиотеке Python (исправлена в Python 3 с использованием объектов байтов напрямую); напишите отчет об ошибке в http://bugs.python.org.