wxpython应用程序中的严重内存泄漏
我对使用ode进行物理建模、使用openGL进行渲染以及使用wx进行UI构建一个相当复杂的wxPython应用程序有一定的了解。一切都进行得很顺利,直到应用程序开始崩溃。在几天没有任何进展之后,我终于注意到我的应用程序正在泄漏内存。我能够提炼出一个小的示例脚本,它以非常惊人的速度泄漏:wxpython应用程序中的严重内存泄漏,python,memory-leaks,wxwidgets,Python,Memory Leaks,Wxwidgets,我对使用ode进行物理建模、使用openGL进行渲染以及使用wx进行UI构建一个相当复杂的wxPython应用程序有一定的了解。一切都进行得很顺利,直到应用程序开始崩溃。在几天没有任何进展之后,我终于注意到我的应用程序正在泄漏内存。我能够提炼出一个小的示例脚本,它以非常惊人的速度泄漏: #------------------------------------------------------------------------------- #------------------------
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
import wx
import wx.propgrid as wxpg
import random
class CoordProperty(wxpg.PyProperty):
def __init__(self, label, name, value=(0,0,0)):
wxpg.PyProperty.__init__(self, label, name)
self.SetValue(value)
def GetClassName(self):
return "CoordProperty"
def GetEditor(self):
return "TextCtrl"
def ValueToString(self, value, flags):
x,y,z = value
return "%f,%f,%f"%(x,y,z)
app = wx.App(False)
frame = wx.Frame(None, -1, "Test")
pg = wxpg.PropertyGridManager(frame)
props = {}
for i in range(1000):
prop_name = "prop_%d"%i
prop = CoordProperty("Coord", prop_name)
pg.Append(prop)
props[prop_name] = prop
def OnTimer(event):
global props
for key in props:
props[key].SetValue((random.random(), random.random(), random.random()))
timer = wx.Timer(frame, 1)
frame.Bind(wx.EVT_TIMER, OnTimer)
timer.Start(10) # 100Hz
frame.Show()
app.MainLoop()
timer.Stop()
该示例创建一个框架,并将wxPropertyGrid放置到其中。它派生一个显示三维坐标值的属性,创建一千个坐标值,然后从以100Hz运行的计时器将每个坐标值更新为随机值。这会在接近10Mb/s的地方泄漏,最终导致崩溃。它通常也会在关机时崩溃
我正在Windows7上使用Python2.7和WX2.9.3.1 msw(经典)
如果我用一个内置属性(如wxpg.FloatProperty)替换派生的CoordProperty,并相应地修改代码,泄漏就会消失
有什么想法吗?或者我应该提交一个wx错误?我甚至可以删除派生属性类中函数ValueToString的定义,应用程序仍然会泄漏。我使用以下代码来计算对象:
def output_memory():
d = defaultdict(int)
for o in gc.get_objects():
name = type(o).__name__
d[name] += 1
items = d.items()
items.sort(key=lambda x:x[1])
for key, value in items:
print key, value
发现你的程序每次事件都会增加1000个元组。因此,当您调用props[key].SetValue()
时,gc尚未收集上一个值。这可能是wxpg的一个bug,我们可以通过使用([x],[y],[z])
保存值来绕过这个bug,这样您就可以在不调用SetValue()的情况下更新值:
以下是完整的代码:
import wx
import wx.propgrid as wxpg
from random import random
import gc
from collections import defaultdict
def output_memory():
d = defaultdict(int)
for o in gc.get_objects():
name = type(o).__name__
d[name] += 1
items = d.items()
items.sort(key=lambda x:x[1])
for key, value in items:
print key, value
class CoordProperty(wxpg.PyProperty):
def __init__(self, label, name):
wxpg.PyProperty.__init__(self, label, name)
self.SetValue(([0],[0],[0]))
def GetClassName(self):
return "CoordProperty"
def GetEditor(self):
return "TextCtrl"
def GetValueAsString(self, flags):
x,y,z = self.GetValue()
return "%f,%f,%f"%(x[0],y[0],z[0])
app = wx.App(False)
frame = wx.Frame(None, -1, "Test")
pg = wxpg.PropertyGridManager(frame)
props = {}
for i in range(1000):
prop_name = "prop_%d"%i
prop = CoordProperty("Coord", prop_name)
pg.Append(prop)
props[prop_name] = prop
def OnTimer(event):
for name, prop in props.iteritems():
value = prop.GetValue()
value[0][0] = random()
value[1][0] = random()
value[2][0] = random()
pg.Refresh()
#output_memory()
timer = wx.Timer(frame, 1)
frame.Bind(wx.EVT_TIMER, OnTimer)
timer.Start(10)
frame.Show()
app.MainLoop()
timer.Stop()
这可能有点令人讨厌,但我非常不喜欢wxpython(基于个人经验)-你可能想换个话题。我支持上面的观点,个人项目让我讨厌任何建议?现在转换还不算晚——我才刚开始两周,几乎什么都可以。即使是Tkinter也没有那么多虫子。这是我碰到的第一个虫子,除此之外,它还很可爱。有趣的是,有多少人似乎讨厌它,但很少是一个好兆头。这是一个伟大的解决办法,谢谢!它解决了泄漏问题,似乎也解决了坠机问题。我有点好奇为什么需要嵌套的元组/列表,但是仅仅修改元组中的条目而不使这些条目成为单元素列表是行不通的。
import wx
import wx.propgrid as wxpg
from random import random
import gc
from collections import defaultdict
def output_memory():
d = defaultdict(int)
for o in gc.get_objects():
name = type(o).__name__
d[name] += 1
items = d.items()
items.sort(key=lambda x:x[1])
for key, value in items:
print key, value
class CoordProperty(wxpg.PyProperty):
def __init__(self, label, name):
wxpg.PyProperty.__init__(self, label, name)
self.SetValue(([0],[0],[0]))
def GetClassName(self):
return "CoordProperty"
def GetEditor(self):
return "TextCtrl"
def GetValueAsString(self, flags):
x,y,z = self.GetValue()
return "%f,%f,%f"%(x[0],y[0],z[0])
app = wx.App(False)
frame = wx.Frame(None, -1, "Test")
pg = wxpg.PropertyGridManager(frame)
props = {}
for i in range(1000):
prop_name = "prop_%d"%i
prop = CoordProperty("Coord", prop_name)
pg.Append(prop)
props[prop_name] = prop
def OnTimer(event):
for name, prop in props.iteritems():
value = prop.GetValue()
value[0][0] = random()
value[1][0] = random()
value[2][0] = random()
pg.Refresh()
#output_memory()
timer = wx.Timer(frame, 1)
frame.Bind(wx.EVT_TIMER, OnTimer)
timer.Start(10)
frame.Show()
app.MainLoop()
timer.Stop()