[Matrix-SIG] Freeing malloc'd array in Python

Aaron Watters aaron@cs.rutgers.edu
Sat, 05 Sep 1998 14:27:13 -0400


I might be wrong, but i think the problem is that
Python keeps a last value reference named _ (underscore);
the del a deletes the *name* a but the object formerly
named a still has another name _, maybe try this instead

a = my_range(10000)
a = None
1

Now the object should be gone.  Another thing that can
happen is the object can get stuck on a traceback object

import sys
sys.last_traceback = None

If you really have a memory leak this should cause problems

from time import sleep
for i in range(100):
     x = my_range(10000)
     print "sleeping"; sleep(2)

...watch the process size from another window.  If it really
is leaking, maybe recomplain.   -- Aaron Watters

Scott M. Ransom wrote:

> Hello,
>
> I am trying to wrap a large pulsar data analysis library written in C so
> that I can use it in Python.  I am using the Numeric extension to
> represent many vectors and matrices that I use.
>
> I am new to Python and do not (yet!) have a good grasp of Python's
> reference counting method of garbage collection -- as you will probably
> see from my code snippet below... ;)
>
> The problem that I am running into is that many of my routines allocate
> arrays using malloc() from 'C' and then return these newly created
> arrays to the user.  In my 'C' routines I simple free() the array in the
> main program when I don't need it anymore.
>
> When I wrap these functions onto Numeric arrays, though, Python does not
> seem to want to free the data that I malloc'd.  For example, using the
> code attached at the bottom of this note and compiled (on Linux) using:
>
> gcc -shared -I/usr/local/include/python1.5 -Wall -fpic -g -O my_range.c
> -o my_range.so
>
> and then imported into Python,  I get the following:
>
> Python 1.5.1 (#8, Sep  3 1998, 19:02:41)  [GCC egcs-2.90.29 980515 (egc
> on linux2
> Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
> >>> from Numeric import *
> >>> from my_range import *
> >>> from sys import *
> >>> a = my_range(10)
> >>> getrefcount(a)
> 2
> >>> a
> array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
> >>> del(a)
> >>> getrefcount(a)
> Traceback (innermost last):
>   File "<stdin>", line 1, in ?
> NameError: a
> >>> a = my_range(100000)
> >>> del(a)
> >>>
>
> del(a) obviously deletes the PyArrayObject, but examining the memory
> usage before and after the a = my_range(100000) and the corresponding
> del(a), it is obvious that Python is not touching the actual data in the
> PyArrayObject -- a bigtime memory leak.
>
> I examined the code in arrayobject.c and arrayobject.h and my initial
> impressions are that I need to somehow manually free the data portion of
> the PyArrayObject when I am finished with it in Python -- before I del()
> the PyArrayObject.  I unfortunately have no idea how to do this.  Do I
> need another helper function in my wrapper code to free() the data in
> the PyArrayObject()?  Or am I missing something even easier?
>
> It seems that I have also been warned (from arrayobject.h) not to use
> PyArray_FromDimsAndData() to create my PyArrayObject (any ideas on how
> to use something else?)
>
> Thanks in advance for your help,
>
> Scott Ransom
>
> ---------my_range.c------------
> #include <stdlib.h>
> #include <stdio.h>
> #include "Python.h"
> #include "arrayobject.h"
>
> /* Here is an example library function that returns an array (a 1D
>    vector).  This is just representative -- the actual functions in my
>    library are much more complicated.  They are all malloc'd,
>    though. */
>
> double * my_range(long n)
> {
>   double *v;
>   long i;
>
>   v = (double *) malloc((size_t) (sizeof(double) * n));
>   if (!v) {
>     printf("\nAllocation error in my_range()\n");
>     exit(1);
>   }
>   for (i=0; i<n; i++) v[i] = (double) i;
>   return v;
> }
>
> /* Here is a representative wrapper function that I am using to
>    interface the above routine with Python (using Numeric arrays). */
>
> static PyObject *wrap_my_range(PyObject *self, PyObject *args)
> {
>   PyObject *obj;
>   PyArrayObject *arr;
>   double *result;
>   long n;
>
>   self = self;
>   if(!PyArg_ParseTuple(args,"O:wrap_my_range",&obj))
>     return NULL;
>   n = PyInt_AsLong((PyObject *)obj);
>   result = my_range(n);
>   arr = (PyArrayObject *)PyArray_FromDimsAndData(1, (int *)&n,
> PyArray_DOUBLE, \
>                                                  (char *) result);
>   if (arr == NULL) return NULL;
>   return (PyObject *)arr;
> }
>
> /* Here are the module initialization functions */
>
> static PyObject *ErrorObject;
> static PyMethodDef my_range_method[] = {
>   { "my_range", wrap_my_range, 1 },
>   { NULL, NULL }
> };
>
> void initmy_range()
> {
>   PyObject *m, *d;
>
>   m = Py_InitModule("my_range", my_range_method);
>   d = PyModule_GetDict(m);
>   import_array();
>   ErrorObject = PyString_FromString("my_range.error");
>   PyDict_SetItemString(d, "error", ErrorObject);
>   if (PyErr_Occurred())
>     Py_FatalError("can't initialize module my_range");
> }
>
> --
> Scott M. Ransom
> Phone:  (580) 536-7215             Address:  703 SW Chaucer Cir.
> email:  ransom@cfa.harvard.edu               Lawton, OK  73505
> PGP Fingerprint: D2 0E D0 10 CD 95 06 DA  EF 78 FE 2B CB 3A D3 53
>
> _______________________________________________
> Matrix-SIG maillist  -  Matrix-SIG@python.org
> http://www.python.org/mailman/listinfo/matrix-sig