2008-12-08 15 views
5

Die folgende Funktion verwendet ein Python-Datei-Handle, liest gepackte Binärdaten aus der Datei ein, erstellt ein Python-Wörterbuch und gibt es zurück. Wenn ich es endlos wiederhole, verbraucht es ständig RAM. Was ist falsch an meiner RefCounting?Warum verliert meine Python C-Erweiterung Speicher?

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); 
} 

Vielen Dank für das Nehmen eines Blickes,

LarsenMTL

Antwort

16

PyDict_New() eine neue Referenz zurückgibt, überprüfen Sie die docs für PyDict. Wenn Sie den Refcount sofort nach dem Erstellen erhöhen, haben Sie zwei Referenzen darauf. Einer wird an den Aufrufer übergeben, wenn Sie ihn als Ergebniswert zurückgeben, aber der andere geht nie weiter.

Sie müssen auch nicht inkrementieren pyTimeList. Es gehört dir, wenn du es erstellst. Allerdings müssen Sie es dekremen, aber Sie dekrementieren es nur einmal, also ist es auch durchgesickert.

Sie müssen auch Py_INCREF unter pyDB anrufen. Es ist eine ausgeliehene Referenz und wird nicht verschwinden, solange Ihre Funktion nicht zurückkehrt, weil sie immer noch in einem unteren Stapelrahmen referenziert wird.

Nur wenn Sie den Verweis in einer anderen Struktur behalten möchten, müssen Sie den Refcount erhöhen.

Cf. die API docs

+0

Torsten, danke, ich habe gerade mehr in deinen 4 Absätzen gelernt, dann habe ich den ganzen Morgen auf die Docs gestarrt. Ich werde alle meine geliehenen gegen neue Referenzen überprüfen. – Mark

5

OT: Verwenden aufeinanderfolgender Aufrufe an PyList_Append ist ein Leistungsproblem. Da Sie wissen, wie viele Ergebnisse, die Sie im Voraus erhalten werden, können Sie verwenden:

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); 
} 

Beachten Sie, dass Sie nicht die refcount von o nach dem Aufruf PyList_SET_ITEM verringern kann, weil es „stiehlt“, eine Referenz. Überprüfen Sie die docs.

2

Ich weiß nicht über Python-C. Meine Erfahrung mit der COM-Referenzzählung besagt jedoch, dass ein neu erstelltes Objekt mit Referenzzählung eine Referenzzahl von hat. Also dein Py_INCREF (pyDB) nach PyArg_ParseTuple (args, "0", & pyDB) und PyObject * pyDBHeader = PyDict_New(); sind die Schuldigen. Ihre Referenzzahlen sind bereits 2.

Verwandte Themen