Python sys.getrefcount(无)滚动到负值

Python sys.getrefcount(无)滚动到负值,python,matplotlib,reference,garbage-collection,Python,Matplotlib,Reference,Garbage Collection,因此,我有一个相当大的python应用程序,它使用wxPython、Matplotlib、Numpy,最初使用pySerial和minimamodbus进行设备通信。该应用程序用于从USB光谱仪读取和绘制数据。在我们决定将modbus和usb之间的通信交换为速度之前,一切都很顺利。我现在正在使用pyusb和libusb0.1后端。除了一个相当烦人的问题外,它在大多数情况下都有效: 致命的Python错误:取消分配无 这是在进行大约20000次测量后弹出的。它并不总是在同一条上,但很接近。在得到错

因此,我有一个相当大的python应用程序,它使用wxPython、Matplotlib、Numpy,最初使用pySerial和minimamodbus进行设备通信。该应用程序用于从USB光谱仪读取和绘制数据。在我们决定将modbus和usb之间的通信交换为速度之前,一切都很顺利。我现在正在使用pyusb和libusb0.1后端。除了一个相当烦人的问题外,它在大多数情况下都有效:

致命的Python错误:取消分配无

这是在进行大约20000次测量后弹出的。它并不总是在同一条上,但很接近。在得到错误后,我在谷歌上搜索了一下,决定在发送cmd和从USB设备接收数据前后在代码中添加sys.getrefcount(None)。我不知道通常预期的数字是多少,但从发送命令之前到提取数据之后,引用计数增加了大约4717个。从读取结束到下一次发送开始,引用计数增加了200000多个。因此,每次测量都会使参考计数增加220000左右

这对我来说似乎是一个惊人的数目,但我不熟悉这里所期望的数字

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

更新:
事实证明,从pySerial切换到pyusb并不是问题的根源。我发现这两个版本的应用程序都有同样的问题,只是现在它变得更加明显了十倍,因为我可以使用pyusb对光谱辐射计进行采样

我已经从代码中删除了所有有意识地使用numpy的内容,尽管我知道matplotib非常频繁地使用numpy,并清理了一些其他方面。这降低了程序的失败率。我现在可以在它失效之前进行近90000次测量,但它仍然失效。在程序开始时使用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>
gc:可收集
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏
gc:可收藏

刚启动这个应用程序就输出了967行gc:collectable“something”。我不完全理解gc库是如何工作的,也不完全理解为什么会出现这些库。我错过了什么明显的东西吗?如何使此垃圾消失?

其中一个包中的某些内容(可能是
pyusb
,因为它是唯一更改的内容)没有正确管理
None
的引用计数。如果没有某种引用泄漏,就不可能对Python对象引用计数进行概括;他们用于引用计数的有符号
size\u t
可以容纳
2**31-1
2**63-1
合法值,即可用内存地址空间大小(字节)的一半(实际上,通常是可用用户模式地址空间的大小,这对您的目的很重要)。由于实际引用它的指针每段使用4或8个字节,即使在没有其他开销的情况下用引用填充所有可用地址,也永远不会有足够的空间来存储可能溢出引用计数字段的引用。这排除了误用的法律代码(比如,将大量引用存储在一个永远不会被清除的缓存中);一定是参考泄漏


很可能每次调用都会多次递增,但以后不会递减。如果您没有自己编写任何Python C扩展或
ctypes
代码来完成这项工作,那么它就是您的软件包之一
pyusb
实际上看起来不像是罪魁祸首;看起来它是用C语言实现的,带有一些
ctypes
的东西,但是用这种方式将真正的Python对象传递给C代码是非常困难的(而且没有理由这样做)。因此,假设您至少使用了一个包,它是作为真正的C扩展实现的,并且是由不理解CPython引用语义的人编写的。我猜不出会是哪一个。

听起来您正在使用的某个模块存在引用泄漏。None的refcount不应该这样做。我认为“不理解CPython引用计数”不是很慷慨——很容易意外地导致手动引用计数错误,特别是当您使用非平凡的控制流编写函数时,或者如果多个程序员在同一个函数上工作时。