2008-12-08 5 views
5

La fonction prend un descripteur de fichier python, lit dans les données binaires emballés à partir du fichier, crée un dictionnaire Python et le renvoie. Si je le boucle en boucle, il consommera continuellement de la RAM. Quel est le problème avec mon RefCounting?Pourquoi ma mémoire Python C Extension des fuites?

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

Merci pour jeter un oeil,

LarsenMTL

Répondre

16

PyDict_New() renvoie une nouvelle référence, vérifiez la docs pour PyDict. Donc si vous augmentez le refcount immédiatement après l'avoir créé, vous avez deux références à celui-ci. On est transféré à l'appelant lorsque vous le retourner en tant que valeur de résultat, mais l'autre ne va jamais aways. Vous n'avez pas besoin d'incrémenter pyTimeList. C'est le vôtre quand vous le créez. Cependant, vous devez le décréter, mais vous ne le décrémentez qu'une seule fois, il y a donc fuite. Vous n'avez pas non plus besoin d'appeler Py_INCREF sur pyDB. Il est une référence empruntée et il ne disparaîtra pas tant que votre fonction ne retourne pas, car il est encore référencé dans un cadre de pile faible. Uniquement si vous souhaitez conserver la référence dans une autre structure, vous devez augmenter le nombre de références.

Cf. le API docs

+0

Torsten, merci, je viens d'apprendre plus dans vos 4 paragraphes, puis j'ai regardé les docs toute la matinée. Je vais vérifier tous mes emprunts vs renvoyer de nouvelles références. – Mark

5

OT: L'utilisation des appels successifs à PyList_Append est un problème de performance. Puisque vous savez combien de résultats que vous obtiendrez à l'avance, vous pouvez utiliser:

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

Observons que vous ne pouvez pas diminuer le refcount de o après avoir appelé PyList_SET_ITEM, parce qu'il « vole » une référence. Vérifiez le docs.

2

Je ne connais pas Python-C. Cependant, mon expérience avec comptage de référence COM dit qu'une référence compté nouvel objet a un compte de référence de . Donc votre Py_INCREF (pyDB) après PyArg_ParseTuple (args, "O", & pyDB) et PyObject * pyDBHeader = PyDict_New(); sont le coupable. Leurs chiffres de référence sont déjà 2.

Questions connexes