Nicht auslagerbarer Speicher wächst beständig

Fragen zu Tkinter.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

numerix hat geschrieben:Wie kommst du zu der Annahme, dass Tkinter hier der Bösewicht ist?
Warum nicht dem Betriebssystem anlasten?
$Betriebssystem ist sicherlich öfter und besser getestet als Tkinter. Desweiteren fällt Tkinter auch sonst so häufig positiv auf weswegen ja ein großteil aller Anwendungen Tkinter GUIs benutzen.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

@HWK:
Hab's jetzt mit Deinem Vorschlag ca. 5 min laufen lassen,
auch hier vermindert sich die Grösse des freien Speichers
ähnlich wie schon vorher beobachtet.

:wink:
yipyip
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

DasIch hat geschrieben:Desweiteren fällt Tkinter auch sonst so häufig positiv auf weswegen ja ein großteil aller Anwendungen Tkinter GUIs benutzen.
Das ist unsachliche Ironie.

Es gibt ganz sicher gute Gründe, Tkinter nicht zu benutzen, vor allem, weil es hinsichtlich seiner Möglichkeiten hinter anderen einschlägigen GUI-Toolkits zurückbleibt und die Optik nicht ganz an das heranreicht, was man heutzutage als uptodate ansieht.

Aber Tkinter ist nicht grundsätzlich problematisch.
BlackJack

Wie sieht's denn aus, wenn ihr mal das `i` weglasst, da werden ja schliesslich ständig neue `int`-Objekte erzeugt. Und die werden vom Code der Speicher für Objekte verwaltet in "Gruppen" verwaltet, und so eine Gruppe wird erst wieder frei gegeben, wenn alle enthaltenen Objekte freigegeben wurden.

Wobei freigegeben auch nur bedeutet, dass das `free()` der C-Laufzeitbibliothek für den Speicherblock aufegrufen wird. Ob *die* dann wiederum den Speicher auch an das Betriebssystem zurück gibt, ist in keiner Weise garantiert. Und wenn sie das nicht tut, ist das vielleicht unangenehm, aber kein Fehler.

Ich denke auch nicht das man die Schuld da so vorschnell bei `Tkinter` suchen sollte. Oh, und Tk könnte auch schuld sein.

Was ich auch schon einmal hatte, war, dass der Speicherverbrauch bei einem Tkinterprogramm mit vielen Grafikveränderungen in kurzer Zeit auf ca. 10 MiB gewachsen ist, bevor er dann nahezu schlagartig wieder um 10 MiB zurück ging. Also vielleicht mal versuchen, ob manuelles Aufrufen des Garbage-Collectors einen sichtbaren Erfolg hat.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

DasIch hat geschrieben:$Betriebssystem ist sicherlich öfter und besser getestet als Tkinter.
Ironie hin oder her, aber das Argument gilt durchaus. Das Linux-Speichermodell ist nun mal schon Jahrelang auf extremste Weise erprobt, das wird bei Tkinter, Tk und Tcl weniger der Fall sein.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

numerix hat geschrieben:Das ist unsachliche Ironie.
Nicht dass ich das nicht schon vorher gewusst hätte. Viel interessanter dass ich damit einen Treffer gelandet habe worauf auch immer, kann das sein ;)
Es gibt ganz sicher gute Gründe, Tkinter nicht zu benutzen[...]
Es würde ja schon reichen Gründe dafür zu haben Tkinter zu benutzen, Gründe die beim ersten Blick nicht schon lächerlich wirken. Tkinter ist nämlich eine abhängigkeit wie jede andere, nicht bei jedem System ist es per default installiert.
Aber Tkinter ist nicht grundsätzlich problematisch.
Deswegen haben ja auch so wenige Probleme damit, allein schon wenn ich die Phänomenale neue Entwicklung im Bereich der Textdarstellung bewundern darf, schwanke ich zwischen Kopfschütteln und Lachanfall.

Letztendlich lässt du den eigentlich Kern aber unbeantwortet, der wie Leonidas festgestellt hat, bleibt.

Ob jetzt Tk, Tkinter oder was auch immer da sonst noch so ein Problem hat letztendlich interessiert mich dass als Nutzer nicht. Da liegt offensichtlich ein Problem vor dass man am gezeigten Python Code nicht wirklich begründen kann.

Das von BlackJack genannte Beispiel bestätigt den Verdacht dass es an Tkinters Speicherverwaltung liegen könnte ebenfalls.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

DasIch hat geschrieben:Es würde ja schon reichen Gründe dafür zu haben Tkinter zu benutzen, Gründe die beim ersten Blick nicht schon lächerlich wirken.
[...]
Deswegen haben ja auch so wenige Probleme damit, allein schon wenn ich die Phänomenale neue Entwicklung im Bereich der Textdarstellung bewundern darf, schwanke ich zwischen Kopfschütteln und Lachanfall.
Dass du kein Freund von Tkinter bist, haben wir jetzt verstanden.

Du musst es ja nicht benutzen. Andere tun es gerne - ohne Kopfschütteln und Lachanfall.
lunar

Leonidas hat geschrieben:
DasIch hat geschrieben:$Betriebssystem ist sicherlich öfter und besser getestet als Tkinter.
Ironie hin oder her, aber das Argument gilt durchaus. Das Linux-Speichermodell ist nun mal schon Jahrelang auf extremste Weise erprobt, das wird bei Tkinter, Tk und Tcl weniger der Fall sein.
Da gilt gar nichts. Tcl ist älter als Linux, und was nun Anfang der Neunziger häufiger genutzt wurde, ist bestenfalls unklar. Vermutungen darüber taugen jedenfalls nicht als Argument.

@DasIch
Deine Abneigung gegen Tkinter ist nicht Thema dieses Threads.

Im Übrigen ist es reichlich absurd, Tkinter als Abhängigkeit mit den verfügbaren Alternativen zu vergleichen. Insbesondere unter Windows, wo man Tkinter bei der Installation explizit deaktivieren muss, während PyQt4 einen großen Download erfordert und Gtk mindestens fünf Installer braucht.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

lunar hat geschrieben:Tcl ist älter als Linux, und was nun Anfang der Neunziger häufiger genutzt wurde, ist bestenfalls unklar. Vermutungen darüber taugen jedenfalls nicht als Argument.
Tatsache ist, dass es heute häufiger benutzt wird und wohl inzwischen einen bestimmten Grad an Zuverläsigkeit erreicht hat. Aber ich finde es etwas lächerlich dass wir Betriebssysteme mit Programmiersprachen vergleichen.
lunar hat geschrieben:Gtk mindestens fünf Installer braucht.
Als ich Windows genutzt habe waren es zwei: GTK+ und PyGTK, wobei der PyGTK-Installer das übliche bdist_wininst-Installer-Ding war, wo man eh nur auf "OK" klicken konnte.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@DasIch: Da finde ich meinen Beitrag jetzt aber unangebracht auf Tkinter verkürzt. Ich würde ehrlich gesagt die "Schuld" eher bei Python's Objektspeicherverwaltung oder der C-Laufzeitbibliothek suchen. Bei Python wegen dem GC für zyklische Abhängigkeiten zwischen Objekten, die vom Programm nicht mehr erreichbar sind, aber nur in gewissen Abständen abgeräumt werden, und die C-Laufzeitbibliothek, weil ein `free()` nicht an das Betriebssystem weitergegeben werden muss.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

BlackJack hat geschrieben:@DasIch: Da finde ich meinen Beitrag jetzt aber unangebracht auf Tkinter verkürzt.
Vielleicht ist er unangebracht verkürzt, kein vielleicht sondern Tatsache ist dass die Qt Variante bei mir dass Verhalten nicht aufweist.

Code: Alles auswählen

from PyQt4 import QtGui
from time import sleep
from thread import start_new_thread

def go():
    start_new_thread(endless, ())

def endless():
    i = 0
    while True:
        label.setText(str(i))
        sleep(.1)
        i += 100

app = QtGui.QApplication([])
label = QtGui.QLabel()
label.show()
go()
app.exec_()
BlackJack

Das hilft bei der Lokalisierung des Problems nicht wirklich weiter. Qt ist C++ wird also Ressourcenverwaltung zu einem Grossteil über RAII abwickeln, d.h. der GC von Python kommt da vielleicht für zyklische Objekte nicht so oft ins Spiel. Und es wird vielleicht eine andere Bibliothek für ``new``/``delete`` und die zugrunde liegende Speicherverwaltung als das `malloc()`/`free()` von der C-Standardbibliothek, die Python verwendet, benutzt.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo Forumfreunde

Ist es einem von euch möglich das Code-Snippet von 'Robin' auf einer Linux-Installation SuSE 11.0 unter Python 2.5 oder 2.6 zu testen?

Bei mir wirft das Code-Snippet bei der obigen Konstellation folgende Exception:
Unhandled exception in thread started by <function endless at 0xb7dac4c4>
Traceback (most recent call last):
File "memory_grow_with_thread_01.py", line 12, in endless
l.configure(text=str(i))
File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1197, in configure
return self._configure('configure', cnf, kw)
File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1188, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: out of stack space (infinite loop?)
Die Exception wird beim Aufruf folgender Tk-Methode ausgelöst:

Code: Alles auswählen

l.configure(text=str(i))
Interessanterweise passiert dies nur in der vom Thread aufgerufenen Funktion 'endless'. Zuerst dache ich es ist ein Stackproblem und stellte vor der Zeile:

Code: Alles auswählen

thread.start_new_thread(endless,())
folgende Anweisung in das Skript:

Code: Alles auswählen

thread.stack_size(32768)
dies brachte aber keine Besserung.

Gruss wuf :wink:
Take it easy Mates!
BlackJack

Das Problem ist einfach, dass man nicht von einem anderen Thread, als dem in dem die `mainloop()` läuft, die GUI verändern sollte. So einfach ist das. Da kann echt alles mögliche passieren. Zwar auch, dass es zufällig funktioniert, aber eben auch so etwas wie Stapelüberläufe und Speicherlecks.

Ich habe den Quelltext ausprobiert (Ubuntu 7.10, Python 2.5) und der Speicherverbrauch steigt stetig. Dann das gleiche ohne Thread, mit der `after()`-Methode, und siehe da: Speicherverbrauch steigt langsam um insgesamt 64 KiB und bleibt dann konstant.

Also noch mal langsam, für die Leute die's nicht glauben wollen, weil's manchmal *scheinbar* funktioniert: Nicht von mehreren Threads auf die GUI zugreifen. Nie und nimmer nicht. Absolut blöde Idee.
Benutzeravatar
Robin
User
Beiträge: 50
Registriert: Dienstag 26. Juni 2007, 10:47

Hi HWK,

es scheint an den Grafikobjekten zu liegen. Wenn ich kein Label verwende, sondern
in der Schleife nur den root.title() aktualisiere, dann wächst es in meinem größeren
Programm moderat.
Fr 15:30 9MB bis Mo 47MB, ein Anstieg auf 40 MB. Ein Absinken war auch in der
Systemüberwachung zu sehen.

Mögliche Ursachen: Grafik-Ausgabe, zu langsamer Rechner oder Fehler im Betriebssystem.
Es bleibt ein Rätsel für mich.

Danke an ALLE, dass Ihr es auch ausprobiert habt und einige den Anstieg auch bestätigen konnten.

Gruß Robin
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo Forumfreunde

Das Problem verhält sich bei mir absolut unberechenbar. Einmal scheint es auf dem Performance-Monitor als wäre das Problem nicht vorhanden ca. 10 Min stabil. Ein anderes mal verhält es sich wie ein JoJo. Der Speicherverbrauch steigt langsam an ca. 500kB springt da schnell wieder zurück. Sieht fast so aus als ob hier der Python Garbage-Collector zyklisch ein Säuberung vornimmt. Im Extremfall kommt es sogar zu einer Exception.
Das ganze könnte man als 'Russisches Roulette' bezeichnen. :D

Übrigens die Version von 'yipyip' arbeite auch bei mir stabil.

Gruss wuf :wink:
Zuletzt geändert von wuf am Montag 1. Dezember 2008, 14:14, insgesamt 1-mal geändert.
Take it easy Mates!
lunar

DasIch hat geschrieben:
BlackJack hat geschrieben:@DasIch: Da finde ich meinen Beitrag jetzt aber unangebracht auf Tkinter verkürzt.
Vielleicht ist er unangebracht verkürzt, kein vielleicht sondern Tatsache ist dass die Qt Variante bei mir dass Verhalten nicht aufweist.
Qt4 verwaltet den Speicher nicht über den Python-GC, sondern über einen eigenen, deterministischen Objektbaum, der Objekte basierend auf Vater-Kind-Verhältnissen zwischen Objekten verwaltet und bereinigt. Das Verhalten von Qt4 in dieser Situation sagt gar nichts über das Verhalten von Tkinter aus.

Im Übrigen ist dein Beispiel ebenso schlecht wie das gepostete Tkinter-Beispiel. Auch in Qt4 sind GUI-Zugriffe aus anderen Threads nicht gut, der kanonische Weg zum Multithreading ist die Nutzung von QtCore.QThread, der ein eigenes Signal auslöst, welches dann mit einem Slot eines Widgets verbunden werden kann. Im Gegensatz zu einfachen Methodenaufrufen sind Signale nämlich threadsicher.
Benutzeravatar
Robin
User
Beiträge: 50
Registriert: Dienstag 26. Juni 2007, 10:47

Anscheinend lag es an der THREAD-Lösung, weshalb der Speicher beständig anstieg.
Mit dieser Lösungsvariante bleibt mein Speicher stabil.

Code: Alles auswählen

import Tkinter as tk
from itertools import count


def start_counter(label,label1,label2):
     counter = count(1)
     def update_func():
         label.config(text=str(counter.next()))
         label1.config(text=str(counter.next()+20))
         label2.config(text=str(counter.next()+40))
         label2.after(10, update_func)
     update_func()


def main():
     root = tk.Tk()
     label = tk.Label(root)
     label.pack()
     label1 = tk.Label(root)
     label1.pack()
     label2 = tk.Label(root)
     label2.pack()

     start_counter(label,label1,label2)
     button = tk.Button(root, text='Beenden', command=root.quit)
     button.pack()
     #label.after(1000,main())
     root.mainloop()


if __name__ == '__main__':
    main()
Antworten