C函数申请的内存,Python没法释放?

2020年12月4日 / 59次阅读 / Last Modified 2020年12月4日
ctypes

本文记录一个内存测试,个人感觉不一定是对的,anyway,先记录下来这个过程。Python调用C函数,C函数中有malloc申请内存,这块内存应该由谁来释放?

先说我的测试结论:还是要调用配套的C函数来释放,Python的del语句,没有释放C函数申请内存的效果。本文内存测试使用了memory_profiler模块。关于在Python中获取C函数返回的指正,请参考:再谈用ctypes获取C函数返回的指针

测试用C代码如下:

$ cat ccc.c
#include 
#include 

typedef struct {
    int a;
    int b;
    float c;
} abc;

abc *init(int a, int b, float c)
{
    abc *pt;
    pt = (abc *)malloc(sizeof(abc));
    pt->a = a;
    pt->b = b;
    pt->c = c;
    return pt;
}

void destroy(abc *pt)
{
    free(pt);
}

init申请内存,destroy释放内存。

先确定内存会因为调用init而增加,测试代码如下(Python):

$ cat mem_test.py
import struct
from ctypes import *
cc = CDLL('./ccc.so')
cc.init.restype = POINTER(c_char*12)


@profile
def test():
    for i in range(50000):
        p = cc.init(10,20,c_float(3.1415))

test()

这段代码就是在test函数中,不停地调用cc.init函数,内存的确在增大:

$ python3 -m memory_profiler mem_test.py
Filename: mem_test.py

Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     7   19.008 MiB   19.008 MiB           1   @profile
     8                                         def test():
     9   20.293 MiB    0.000 MiB       50001       for i in range(50000):
    10   20.293 MiB    1.285 MiB       50000           p = cc.init(10,20,c_float(3.1415))

如果测试代码增加调用destroy接口,代码修改如下:

$ cat mem_test.py
import struct
from ctypes import *
cc = CDLL('./ccc.so')
cc.init.restype = POINTER(c_char*12)


@profile
def test():
    for i in range(50000):
        p = cc.init(10,20,c_float(3.1415))
        cc.destroy(p)

test()

再次观察内存的情况,内存全都被释放了:

$ python3 -m memory_profiler mem_test.py
Filename: mem_test.py

Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     7   19.324 MiB   19.324 MiB           1   @profile
     8                                         def test():
     9   19.324 MiB    0.000 MiB       50001       for i in range(50000):
    10   19.324 MiB    0.000 MiB       50000           p = cc.init(10,20,c_float(3.1415))
    11   19.324 MiB    0.000 MiB       50000           cc.destroy(p)

看不到内存增加了。。。

再次修改测试代码,使用python的del语句:

$ cat mem_test.py
import struct
from ctypes import *
cc = CDLL('./ccc.so')
cc.init.restype = POINTER(c_char*12)


@profile
def test():
    for i in range(50000):
        p = cc.init(10,20,c_float(3.1415))
        del p

test()

测试结果如下:

$ python3 -m memory_profiler mem_test.py
Filename: mem_test.py

Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     7   19.160 MiB   19.160 MiB           1   @profile
     8                                         def test():
     9   20.504 MiB    0.000 MiB       50001       for i in range(50000):
    10   20.504 MiB    1.344 MiB       50000           p = cc.init(10,20,c_float(3.1415))
    11   20.504 MiB    0.000 MiB       50000           del p

内存还是一路上扬......del只是删除的python解释器中的变量,C函数申请的内存还在。

下图是用mprof测试内存得到的,蓝色的[]之间是test函数的测试时间段,一路上扬,del p语句没有让内存降下来:

用mprof测试内存
用mprof测试内存

C函数申请的内存被释放后,Python对应的对象也不能再访问,准确地说,应该是我们还可以继续访问Python对象,但是内部的值已经不对了。

测试代码(Python):

import struct
from ctypes import *
cc = CDLL('./ccc.so')
cc.init.restype = POINTER(c_char*12)

p = cc.init(10,20,c_float(3.1415))
print(struct.unpack('iif',p.contents))

cc.destroy(p)
print(struct.unpack('iif',p.contents))

执行结果如下:

$ python3 mem_test.py
(10, 20, 3.1414999961853027)
(-1106763040, 22012, -0.1250002384185791)
$ python3 mem_test.py
(10, 20, 3.1414999961853027)
(-1451334944, 21878, -5.4678538172897584e-14)

内存释放后,Python的对象还在,但是内容已经不可再使用。

单从本文测试来看,C申请的内存,只能C来释放,Python对象与这些内存有关联,但是Python没有释放的接口。Python的Garbage Collection机制,应该是针对Python对象的,也处理不到C函数申请的内存区域。

-- EOF --

本文链接:https://www.pynote.net/archives/2965

留言区

邮箱地址不会被公开。 必填项已用*标注


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top