近日需要使用到某同学使用c++封装的python扩展模块,搭配好环境,写上代码,跑起来,top一下,发现内存一直在涨。。。
于是找到源代码,大致瞅了下,发现貌似是一些引用计数的问题
py的封装最不好控制的就是引用计数了,很多时候不知道啥时候Py_INCREF,啥时候Py_DECREF,这也是为什么会存在 boost.python,pycxx的原因,诸如boost.python,pycxx的模块把这些问题都给隐藏了
ok,现在开始验证,在调用的py代码里面加上:
print len(gc.get_objects())
把程序run起来,发现数目一直在涨,目前可以确定:内存确实存在泄露。下一步,定位是哪里泄露。
看代码,发现返回结果的时候,是打包成了一个新的result结构体,把这个结构体注释掉,仅仅调用segment函数,重新编译,仍然有泄露。
看来问题不是在result结构体,研究segment的代码,发现里面的核心功能就是一个切词,把获取切词结果的函数注释掉,发现仍然有问题。
于是怀疑是使用的切词的库有问题,如果找到所使用的版本,发现这个的四位版本list中确实有个版本fix了内存泄露的问题,那么问题很明显了:使用了一个内存泄露的c库。
心中一阵狂喜,嗯嗯,问题应该可以解决了。
于是更新所使用的c库,重新build出so,跑上py代码,top下观看内存,咦,怎么内存还是一直在涨而object不变?
很奇怪,object数目不变,就表示没有内存泄露,但是内存却一直在涨。。。
怀疑是代码写的不对,于是改用了两种方法来做:
1,把写的c扩展的代码,改用类实现
2,写个c的简单的封装,调用py的ctypes来使用这个so
写完后验证了下,发现object仍然不变,但内存一直在涨。这说明:
1,内存没泄露---否则object数目会一直增长
2,很可能是py自己获取了内存后,就一直未释放(这里的未释放不是指不会释放该对象,而是指不会释放管理这个对象的object指针)
google了下,发现很多人都曾验证过,py确实如此,比如 这里 ,另外,这里 也有一篇文章说为什么py不释放内存。
于是想,好了,事情到此为止了,好歹有个交代了。于是准备放弃研究这个问题,把其归为py自己的内存管理的问题:py自己的pool中获取到内存就不会释放。
原本以为事情就到此可以结束