2008-12-08 17 views
5

नीचे दिया गया फ़ंक्शन एक पायथन फ़ाइल हैंडल लेता है, फ़ाइल से पैक किए गए बाइनरी डेटा में पढ़ता है, एक पायथन शब्दकोश बनाता है और इसे वापस करता है। अगर मैं इसे अंतहीन रूप से लूप करता हूं, तो यह लगातार राम का उपभोग करेगा। मेरे 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); 
} 

एक नज़र लेने के लिए धन्यवाद,

LarsenMTL

उत्तर

16

PyDict_New() एक नया संदर्भ देता है, PyDict के लिए docs की जाँच करें। इसलिए यदि आप इसे बनाने के तुरंत बाद रीफ्रॉउंट बढ़ाते हैं, तो आपके पास इसके दो संदर्भ हैं। जब आप इसे परिणाम मूल्य के रूप में वापस करते हैं तो कॉलर को स्थानांतरित किया जाता है, लेकिन दूसरा कभी भी रास्ते नहीं जाता है।

आपको pyTimeList को बढ़ाने की आवश्यकता नहीं है। जब आप इसे बनाते हैं तो यह तुम्हारा है। हालांकि, आपको इसे कम करने की आवश्यकता है, लेकिन आप इसे केवल एक बार घटाते हैं, इसलिए यह भी लीक हो जाता है।

आपको पर pyDB पर कॉल करने की आवश्यकता नहीं है। यह उधारित संदर्भ है और जब तक आपका फ़ंक्शन वापस नहीं आता है, तब तक यह दूर नहीं जाएगा, क्योंकि यह अभी भी निचले ढेर फ्रेम में संदर्भित है।

केवल अगर आप किसी अन्य संरचना में संदर्भ रखना चाहते हैं, तो आपको रीफ्रॉउंट को घुमाने की आवश्यकता है।

सीएफ। API docs

+0

टोरस्टन, धन्यवाद, मैंने अभी आपके 4 अनुच्छेदों में और अधिक सीखा है, फिर मैंने सुबह सुबह दस्तावेज़ों पर घूरते हुए देखा। मैं अपने सभी उधारित बनाम नए संदर्भों की जांच करूंगा। – Mark

5

ओटी: 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); 
} 

गौर करें कि आप PyList_SET_ITEM बुला के बाद o की refcount कमी नहीं हो सकता है, क्योंकि यह "चुरा" एक संदर्भ। docs देखें।

2

मुझे पायथन-सी के बारे में पता नहीं है। हालांकि, COM संदर्भ गिनती के साथ मेरा अनुभव कहता है कि एक नव निर्मित संदर्भ-गिनती ऑब्जेक्ट की संदर्भ संख्या है। तो PyArg_ParseTuple के बाद आपका Py_INCREF (pyDB) (args, "O", & pydb) और PyObject * pyDBHeader = PyDict_New(); अपराधी हैं उनके संदर्भ संख्या पहले से ही हैं 2.

संबंधित मुद्दे