2016-02-12 22 views
1

因此,我有一個相當大的python應用程序使用wxPython,Matplotlib,Numpy,並且最初使用pySerial和minimalmodbus進行設備通信。該應用程序用於從USB分光輻射度計讀取和繪製數據。直到我們決定將通信從modbus轉換到usb以獲得速度之前,所有操作都很順利。我現在使用libusb0.1後端的pyusb。它的工作原理與一個相當惱人的例外大部分:sys.getrefcount(無)滾動到負值

致命的Python錯誤:解除分配無

這取約20000測量後彈出。它並不總是在同一個平臺上,但它很接近。得到錯誤後,我做了一些Google搜索,並決定在發送cmd並從USB設備接收數據之前和之後,在我的代碼中添加sys.getrefcount(None)。我不知道通常預期的數字是多少,但是在發出命令之前增加了大約4717個參考計數,直到數據被拉出。從閱讀結束到下一次發送開始,參考計數增加了20多萬。所以每次測量都將參考計數增加了大約22萬!

這似乎對我來說是一個無足輕重的金額,但我不熟悉這裏預期的數字種類。

問題是,看起來無論變量是否保存'None'的引用計數都是32位有符號整數。經過約9000次測量後,參考計數從2147483647溢出至-2147483648。然後以上述相同的速率穩步增加,直到它達到零,並殺死程序,出現上述的致命Python錯誤:釋放無。

更新:
事實證明,從pySerial切換到pyusb不是問題的根源。我發現這兩個版本的應用程序都有同樣的問題,現在由於我可以使用pyusb對光譜輻射計進行採樣的速度比現在要快10倍。

我已經從我的代碼中刪除了numpy的所有有意使用,雖然我知道matplotib使用numpy很重,並清理了其他一些區域。這降低了程序失敗的速度。現在我可以在事情失敗之前獲得近9萬次測量結果,但仍然失敗。在程序開始時使用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'something something'。我不完全瞭解gc庫是如何工作的,或者爲什麼這些會顯示出來。我錯過了明顯的東西嗎?我如何讓這些垃圾消失?

+1

聽起來像你正在使用的一些模塊有一個參考泄漏。 「無」的補償不應該這樣做。 – user2357112

回答

1

其中一個軟件包中的東西(可能是pyusb,因爲它是唯一改變的)不能正確管理None的引用計數。如果沒有引用某種類型的引用漏洞,就會導致Python對象引用計數環繞變得不可能;它們用於引用計數的簽名size_t可以容納2**31 - 12**63 - 1合法值,是可用內存地址空間大小的一半(以字節爲單位)(實際上,通常爲可用用戶模式地址空間的大小,這對於您的計算目的)。由於實際引用它的指針使用4或8個字節,因此即使您使用引用填充了所有可用地址而沒有其他開銷,仍然沒有足夠的空間來存儲可能溢出引用計數字段的引用。這就排除了被濫用的法律代碼(比如說,將大量的引用存儲在永遠不會被清除的緩存中);它必須是參考泄漏。

大概每次調用都會增加很多次,並且以後不會遞減。如果您自己沒有編寫任何Python C擴展或​​代碼來執行此操作,那麼它就是您的軟件包之一。 pyusb實際上似乎不是一個可能的罪魁禍首;它看起來像在C中實現了一些​​的東西,但是很難將真正的Python對象以這種方式傳遞給C代碼(並且沒有理由這麼做)。所以大概你至少有一個正在使用的軟件包被實現爲一個真正的C擴展,並且是由不瞭解CPython引用計數語義的人編寫的。我無法開始猜測會是哪一個。

+1

我認爲「不理解CPython引用計數」並不是非常慷慨 - 很容易讓意外手動引用計數錯誤,特別是如果您正在編寫具有非平凡控制流的函數,或者多個程序員在相同工作功能。 –