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 に答える