22

我一直在编写与扫描目录有关的脚本,并在调用 os.path.isdir 时注意到严重的内存泄漏,因此我尝试了以下代码段:

def func():
    if not os.path.isdir('D:\Downloads'):
        return False
while True:
    func()

在几秒钟内,Python 进程达到了 100MB RAM。

我试图弄清楚发生了什么。似乎只有当路径确实是有效的目录路径时,巨大的内存泄漏才有效(这意味着不执行“return False”)。此外,看看相关调用(如 os.path.isfile)中发生了什么也很有趣。

想法?

编辑: 我想我正在做某事。虽然 isfile 和 isdir 是在 genericpath 模块中实现的,但在 Windows 系统上 - isdir 是从内置的 nt 中导入的。所以我不得不下载 2.7.3 的源代码(我早就应该这样做了......)。

经过一番搜索,我在\Modules\posixmodule.c中找到了posix__isdir函数,我认为它是从 nt 导入的 'isdir' 函数。

这部分功能(和评论)引起了我的注意:

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') - 没有任何内存泄漏。哈哈。

4

1 回答 1

8

根本原因是调用非Unicode路径中PyMem_Free的变量失败:path

    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()将分配所需大小的缓冲区,将编码数据复制到此缓冲区并调整 *buffer 以引用新分配的存储空间。调用者负责PyMem_Free()在使用后调用以释放分配的缓冲区

这是 Python 标准库中的一个错误(在 Python 3 中通过直接使用字节对象修复);在http://bugs.python.org提交错误报告。

于 2012-09-29T05:30:25.860 回答