代码之家  ›  专栏  ›  技术社区  ›  Greg Dunn

我应该如何释放在类型映射中为结构的参数化数组分配的内存?

  •  1
  • Greg Dunn  · 技术社区  · 8 年前

    我正在使用SWIG为Python包装一个C库。C方法之一需要 指向结构缓冲区和要填充的元素数的指针 元素数和返回值是填充结构的元组。

      C     : int fill_widgets(widget_t *buffer, int num_widgets);
      Python: fill_widgets(num_widgets) -> (widget, widget,...)
    

    我已经写了一些类型图,这些类型图可以按我想要的那样工作-类型图分配 如果/何时/如何需要释放typemap中分配的内存。

    函数退出,但我相信返回到Python的结构仍在使用 物理内存(即内存没有被复制,我只得到一个代理指针 指向缓冲区中每个元素的代理指针—释放它们没有意义 全部的在这两种情况下,Python最终都会在一个更晚的 free()调用。

    当结构的Python元组超出范围时,如何释放内存?

    下面是一个简单的SWIG界面,它演示了我所拥有的:

    %module "test"
    
    %typemap (in, numinputs=1) (BUF, NUM){
        $2 = PyInt_AsLong($input);
        $1 = ($1_type)calloc($2, sizeof($*1_type));
    }
    
    %typemap (argout) (BUF, NUM){
        PyObject *tpl = PyTuple_New($2);
        for ($2_ltype i=0; i<$2; i++)
        {
            PyTuple_SET_ITEM(tpl, i, SWIG_NewPointerObj(&$1[i], $1_descriptor, 0));
        }
        $result = SWIG_Python_AppendOutput($result, tpl);
    }
    
    %typemap (freearg) (BUF, NUM){
        //free($1);
    }
    
    %apply (BUF, NUM) {(widget_t *buf, int num_widgets)};
    
    %inline {
    typedef struct {int a; int b;} widget_t;
    
    int fill_widgets(widget_t *buf, int num_widgets)
    {
        for(int i=0; i<num_widgets; i++)
        {
            buf[i].a = i;
            buf[i].b = 2*i;
        }
        return num_widgets;
    }
    }
    

    以及构建/运行示例:

    $ swig -python test.i
    $ gcc -I/path/to/python2.7 -shared -lpython2.7 test_wrap.c  -o _test.so
    $ python
    >>> import test
    >>> _,widgets = test.fill_widgets(4)
    >>> for w in widgets: print w.a, w.b
    ... 
    0 0
    1 2
    2 4
    3 6
    >>> 
    

    C中fill_小部件的使用示例:

    int main()
    {
        widget_t widgets[10];  // or widget_t *widgets = calloc(10, sizeof(widget_t))
        fill_widgets(widgets, 10);
    }
    
    1 回复  |  直到 8 年前
        1
  •  1
  •   Flexo - Save the data dump sunny moon    8 年前

    有趣的是,您有1个缓冲区,但创建了N个Python代理对象,所有这些对象都位于单个缓冲区中。

    假设您不愿意将对象从缓冲区中复制出来,那么您将以1:1的比例分配给Python代理对象映射,然后处理原始缓冲区,我们基本上有一个解决方案需要解决。这里的目的是确保每个Python对象也包含对拥有内存的对象的引用。这样我们可以保持引用计数高,并且只有在确定没有人可能仍然指向它时才释放内存。

    calloc )然后让其他代理对象不拥有内存,但保留对内存的引用。

    PyObject_SetAttrString 除了第一个项目外,所有项目都要保留一个参考。结果是这样的:

    %typemap (argout) (BUF, NUM){
        PyObject *tpl = PyTuple_New($2);
        for ($2_ltype i=0; i<$2; i++)
        {
            PyObject *item = SWIG_NewPointerObj(&$1[i], $1_descriptor, 0==i?SWIG_POINTER_OWN:0);
            if (i) {
                PyObject_SetAttrString(item,"_buffer",PyTuple_GET_ITEM(tpl, 0));
            } 
            PyTuple_SET_ITEM(tpl, i, item);
        }
        $result = SWIG_Python_AppendOutput($result, tpl);
    }
    

    我们可以交互检查参考计数是否符合预期:

    In [1]: import test
    
    In [2]: import sys
    
    In [3]: a,b=test.fill_widgets(20)
    
    In [4]: sys.getrefcount(b[0])
    Out[4]: 21
    
    In [5]: sys.getrefcount(b[1])
    Out[5]: 2
    
    In [6]: b[1]._buffer
    Out[6]: <test.widget_t; proxy of <Swig Object of type 'widget_t *' at 0xb2118d10> >
    
    In [7]: b[1]._buffer == b[0]
    Out[7]: True
    
    In [8]: x,y,z = b[0:3]
    
    In [9]: del a
    
    In [10]: del b
    
    In [11]: sys.getrefcount(x)
    Out[11]: 4
    
    In [12]: sys.getrefcount(y)
    Out[12]: 2
    
    In [13]: sys.getrefcount(z)
    Out[13]: 2
    
    In [14]: del x
    
    In [15]: sys.getrefcount(y._buffer)
    Out[15]: 3