2008-12-08 11 views
5

La función siguiente toma un controlador de archivo python, lee datos binarios empaquetados del archivo, crea un diccionario de Python y lo devuelve. Si lo repito sin parar, consumirá RAM continuamente. ¿Qué pasa con mi RefCounting?¿Por qué mi extensión Python C pierde memoria?

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

Gracias por echar un vistazo,

LarsenMTL

Respuesta

16

PyDict_New() devuelve una nueva referencia, comprobar el docs para PyDict. Entonces, si aumenta el recuento inmediatamente después de crearlo, tiene dos referencias. Uno se transfiere a la persona que llama cuando lo devuelve como valor de resultado, pero el otro nunca se va.

Tampoco necesita aumentar pyTimeList. Es tuyo cuando lo creas. Sin embargo, necesitas decrefificarlo, pero solo lo decretaste una vez, por lo que se filtró también.

Tampoco necesita llamar al Py_INCREF en pyDB. Es una referencia prestada y no desaparecerá mientras su función no vuelva, porque todavía se hace referencia en un marco de pila inferior.

Solo si desea mantener la referencia en otra estructura en alguna parte, necesita aumentar el recuento.

Cf. API docs

+0

Torsten, gracias, acabo de aprender más en sus 4 párrafos, luego miré a los documentos toda la mañana. Revisaré todas mis referencias prestadas frente a nuevas. – Mark

5

OT: El uso de llamadas sucesivas a PyList_Append es un problema de rendimiento. Puesto que usted sabe cuántos resultados que obtendrá con antelación, puede utilizar:

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

Observe que es posible que no disminuya la refcount de o después de llamar PyList_SET_ITEM, porque "roba" una referencia. Compruebe el docs.

2

No sé de Python-C. Sin embargo, mi experiencia con el recuento de referencias COM dice que un objeto recuento de referencia recién creado tiene un recuento de referencia de . Entonces su Py_INCREF (pyDB) después de PyArg_ParseTuple (args, "O", & pyDB) y PyObject * pyDBHeader = PyDict_New(); son los culpables Sus recuentos de referencia ya son 2.

Cuestiones relacionadas