sys.getrefcount(无)滚动到负值

时间:2016-02-12 00:45:23

标签: python matplotlib reference garbage-collection

所以我有一个相当大的python应用程序使用wxPython,Matplotlib,Numpy,最初使用pySerial和minimalmodbus进行设备通信。该应用程序用于从USB光谱辐射计读取和绘制数据。一切都运行良好,直到我们决定将通信从modbus交换到usb以获得速度。我现在正在使用带有libusb0.1后端的pyusb。除了相当恼人的外,它大部分都有效:

致命Python错误:取消分配无

在进行约20,000次测量后会弹出。它并不总是在同一个但它很接近。得到错误后,我做了一些谷歌搜索,并决定在发送cmd并从USB设备接收数据之前和之后在我的代码中放入一个sys.getrefcount(无)。我不知道通常会有多少数字,但是在发送命令之前,直到数据被拉出之后,引用计数增加了大约4717。从读取结束到下一次发送开始,引用计数增加了200,000多一点。因此,每次测量都会使参考计数增加约220000!

这对我来说似乎是一个令人发指的数字,但后来我不熟悉这里预期的数字种类。

问题在于,似乎任何变量保持'None'的引用计数是32位有符号整数。在大约9000次测量之后,参考计数从2147483647溢出到-2147483648。然后它以与上面相同的速率稳步增加,直到它达到零,并使用前面提到的致命Python错误杀死程序:释放无。

更新:
事实证明,从pySerial切换到pyusb不是问题的根源。我发现应用程序的两个版本都有同样的问题,因为我可以使用pyusb对光谱辐射计进行采样的速度,因此它的亮度要高出十倍。

我已经从代码中删除了所有有意识的numpy用法,虽然我知道matplotib使用numpy非常重要并且清理了其他一些区域。这降低了程序失败的速度。我现在可以在事情失败之前获得近90,000次测量,但它仍然会失败。在程序开始时使用gc库并设置gc.set_debug(gc.DEBUG_LEAK)帮助我找到了许多需要修复的区域。该程序吐出了许多似乎来自matplotlib的引用。它经常重复这个序列:

gc: collectable <MarkerStyle 05575AB0>
gc: collectable <dict 066C8030>
gc: collectable <instancemethod 0532EB70>
gc: collectable <Affine2D 066BE690>
gc: collectable <WeakValueDictionary instance at 066AE300>
gc: collectable <weakref 066C51B0>
gc: collectable <function 066C11F0>
gc: collectable <tuple 066BE350>
gc: collectable <dict 066C85D0>
gc: collectable <list 05134760>
gc: collectable <set 05597C60>
gc: collectable <dict 066C8390>
gc: collectable <MarkerStyle 066BE790>
gc: collectable <dict 066C8540>
gc: collectable <instancemethod 0532EB48>
gc: collectable <Affine2D 066BE7B0>
gc: collectable <WeakValueDictionary instance at 066AEEB8>
gc: collectable <weakref 066C5210>
gc: collectable <function 066C1270>
gc: collectable <tuple 066BE5B0>
gc: collectable <dict 066C8AE0>
gc: collectable <list 051346E8>
gc: collectable <set 05597A80>
gc: collectable <dict 066C88A0>
gc: collectable <MarkerStyle 066BE850>
gc: collectable <dict 066C8A50>
gc: collectable <instancemethod 0532E760>
gc: collectable <Path 066BE870>
gc: collectable <IdentityTransform 066BE890>
gc: collectable <WeakValueDictionary instance at 066C9148>
gc: collectable <weakref 066C5270>
gc: collectable <function 066C12F0>
gc: collectable <tuple 066BE050>
gc: collectable <dict 066C8D20>
gc: collectable <list 05134530>
gc: collectable <set 05597990>
gc: collectable <dict 066C8F60>

刚启动应用程序会喷出967行gc:collectable'东西'。我不完全理解gc库如何工作或者为什么会出现这些库。我错过了一些明显的东西吗如何让这些垃圾消失?

1 个答案:

答案 0 :(得分:1)

其中一个软件包中的某些内容(可能是pyusb,因为它是唯一发生变化的内容)并未正确管理None的引用计数。如果没有某种类型的引用泄漏,它几乎不可能导致Python对象引用计数的回绕;他们用于引用计数的签名size_t可以保存2**31 - 12**63 - 1合法值,是可用内存地址空间大小的一半(以字节为单位)(实际上,通常是可用的用户模式地址空间,这是您的目的。由于实际引用它的指针使用4或8个字节一块,即使你用没有其他开销的引用填充所有可用地址,你仍然没有足够的空间来存储可能溢出引用计数字段的引用。这排除了被滥用的法律代码(比如,在缓存中存储大量永远不会被清除的引用);它必须是参考泄漏。

据推测,每次通话都会增加很多次,而不会在以后递减。如果您没有自己编写任何Python C扩展或ctypes代码来执行此操作,那么它就是您的一个软件包。 pyusb实际上似乎不是一个可能的罪魁祸首;看起来它在C中实现了一些ctypes的东西,但是用这种方式将真正的Python对象传递给C代码是非常困难的(并且没有理由这样做)。因此,据推测,您至少有一个正在使用的程序包被实现为真正的C扩展,并且是由不了解CPython引用计数语义的人编写的。我不能开始猜测会是哪一个。