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语句没有让内存降下来:
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
Ctrl+D 收藏本页
©Copyright 麦新杰 Since 2019 Python笔记