6

以下の関数は、Python ファイル ハンドルを受け取り、ファイルからパックされたバイナリ データを読み取り、Python 辞書を作成して返します。際限なくループすると、RAM が継続的に消費されます。RefCounting の何が問題になっていますか?

static PyObject* __binParse_getDBHeader(PyObject *self, PyObject *args){

PyObject *o; //generic object
PyObject* pyDB = NULL; //this has to be a py file object

if (!PyArg_ParseTuple(args, "O", &pyDB)){
    return NULL;
} else {
    Py_INCREF(pyDB);
    if (!PyFile_Check(pyDB)){
        Py_DECREF(pyDB);
        PyErr_SetString(PyExc_IOError, "argument 1 must be open file handle");
        return NULL;
    }
}

FILE *fhDB = PyFile_AsFile(pyDB);

long offset = 0;
DB_HEADER *pdbHeader = malloc(sizeof(DB_HEADER));
fseek(fhDB,offset,SEEK_SET); //at the beginning
fread(pdbHeader, 1, sizeof(DB_HEADER), fhDB );
if (ferror(fhDB)){
    fclose(fhDB);
    Py_DECREF(pyDB);
    PyErr_SetString(PyExc_IOError, "failed reading database header");
    return NULL;
}
Py_DECREF(pyDB);

PyObject *pyDBHeader = PyDict_New();
Py_INCREF(pyDBHeader);

o=PyInt_FromLong(pdbHeader->version_number);
PyDict_SetItemString(pyDBHeader, "version", o);
Py_DECREF(o);

PyObject *pyTimeList = PyList_New(0);
Py_INCREF(pyTimeList);

int i;
for (i=0; i<NUM_DRAWERS; i++){
    //epochs
    o=PyInt_FromLong(pdbHeader->last_good_test[i]);
    PyList_Append(pyTimeList, o);
    Py_DECREF(o);
}
PyDict_SetItemString(pyDBHeader, "lastTest", pyTimeList);
Py_DECREF(pyTimeList);

o=PyInt_FromLong(pdbHeader->temp);
PyDict_SetItemString(pyDBHeader, "temp", o);
Py_DECREF(o);

free(pdbHeader);
return (pyDBHeader);
}

ご覧いただきありがとうございます。

ラーセンMTL

4

3 に答える 3

17

PyDict_New()は新しい参照を返します。ドキュメントで を確認してくださいPyDict。そのため、作成直後に refcount を増やすと、2 つの参照が作成されます。1 つは結果値として返されるときに呼び出し元に転送されますが、もう 1 つは消えることはありません。

また、 incref も必要ありませんpyTimeList。あなたがそれを作成したとき、それはあなたのものです。ただし、逆参照する必要がありますが、一度だけ逆参照するため、リークも発生します。

Py_INCREF電話をかける必要もありませんpyDB。これは借用された参照であり、関数が返されない限り消えません。これは、下位のスタック フレームで参照されているためです。

参照を別の構造体のどこかに保持したい場合にのみ、refcount を増やす必要があります。

参照。APIドキュメント

于 2008-12-08T20:04:32.643 に答える
5

OT: への連続した呼び出しを使用するPyList_Appendと、パフォーマンスの問題が発生します。事前に得られる結果の数がわかっているので、次を使用できます。

PyObject *pyTimeList = PyList_New(NUM_DRAWERS);
int i;
for (i=0; i<NUM_DRAWERS; i++){
    o = PyInt_FromLong(pdbHeader->last_good_test[i]);
    PyList_SET_ITEM(pyTimeList, i, o);
}

参照を「盗む」ため、oを呼び出した後に の refcount を減らさないことに注意してください。ドキュメントPyList_SET_ITEMを確認してください。

于 2008-12-08T20:12:44.017 に答える
3

Python-Cについては知りません。ただし、COM参照カウントに関する私の経験では、新しく作成された参照カウントオブジェクトの参照カウントは1です。したがって、PyArg_ParseTuple(args、 "O"、&pyDB)およびPyObject * pyDBHeader = PyDict_New();の後のPy_INCREF(pyDB)犯人です。それらの参照カウントはすでに2です。

于 2008-12-08T20:18:09.497 に答える