У меня есть скрипт Python, который обрабатывает связь Modbus. Одной из функций, которую я добавил, был «график», который показывает время отклика вместе с цветной линией, которая указывает, был ли ответ успешным, имел ли исключение или ошибку. График - это просто прокручиваемый виджет холста от Tkinter.
После построения графика определенного количества строк старые строки будут удалены, а затем добавлена новая в конец. Для этого примера у меня установлено значение 10, что означает, что на холсте никогда не будет более 10 строк одновременно.
Код работает правильно, но где-то в этой функции есть утечка памяти. Я позволил ему проработать около 24 часов, а через 24 часа потребовалось примерно в 6 раз больше памяти. Функция является частью большего класса.
Мое текущее предположение состоит в том, что мой код заставляет размер холста постоянно «расширяться», что медленно пожирает память.
self.lineList = []
self.xPos = 0
def UpdateResponseTimeGraph(self):
if not self.graphQueue.empty():
temp = self.graphQueue.get() #pull from queue. A separate thread handles calculating the length and color of the line.
self.graphQueue.task_done()
lineName = temp[0] #assign queue values to variables
lineLength = temp[1]
lineColor = temp[2]
if len(self.lineList) >= 10: #if more than 10 lines are on the graph, delete the first one.
self.responseTimeCanvas.delete(self.lineList[0])
del self.lineList[0]
#Add line to canvas and a list so it can be referenced.
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-lineLength,
fill=lineColor, outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.graphFrame.after(10, self.UpdateResponseTimeGraph)
Одним решением может быть возврат к началу графа, как только будет достигнут предел, но я бы предпочел этого не делать, так как это может привести к путанице в начале графа. Обычно у меня гораздо больше ответов, чем 10.
EDIT:
Я все еще занимаюсь отслеживанием и ошибками, но похоже, что утечка памяти может быть устранена предложением Брайана, если атрибуты строки не изменяются с помощью itemconfig. Код ниже должен быть в состоянии работать как есть, если вы используете Python 2.7, измените оператор импорта с tkinter на Tkinter (строчные и прописные t). В этом коде будет утечка памяти. Закомментируйте строку itemconfig, и она будет удалена.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#{:02x}{:02x}{:02x}'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
#if i comment out the line below the memory leak goes away.
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()