Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Nicht genügend Quoten, um den angeforderten Dienst auszuführen bei Dauerbetrieb
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

Nicht genügend Quoten, um den angeforderten Dienst auszuführen bei Dauerbetrieb

beantworten | zitieren | melden

Ich habe hier eine WPF Slideshow, die im 3-Sekunden Takt einem von zwei Image-Steuerelementen eine BitmapImage als Source zuweist, welches aus Bildern, die auf der Festplatte liegen, erzeugt wird. Das Programm läuft zwar auf einem hardwareschwachen Tablet, läuft aber über Stunden hinweg problemlos durch. Ca. einmal am Tag passiert es jedoch, dass das Programm einfach abstürzt. Ich hab das mal untersucht, indem ich bisher nicht-gefangene Exceptions nun zentral abfange und in einer Log-Datei speichere. Dabei kam folgendes raus:
Fehler
DispatcherUnhandledException: System.IO.IOException: Nicht genügend Quoten, um den angeforderten Dienst auszuführen.

bei System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
bei System.IO.__ConsoleStream.Write(Byte[] buffer, Int32 offset, Int32 count)
bei System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
bei System.IO.StreamWriter.Write(String value)
bei System.IO.TextWriter.Write(Int32 value)
bei System.IO.TextWriter.WriteLine(Int32 value)
bei System.IO.TextWriter.SyncTextWriter.WriteLine(Int32 value)
bei System.Console.WriteLine(Int32 value)
bei P_Slideshow_Vorabversion.MainWindow.<ImageChangeTimer_Tick>d__24.MoveNext()
--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
bei System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_0(Object state)
bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
bei System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

Das ganze lässt sich auf diese Codestelle zurückführen, welche im Timer-Tick Event alle 3 Sekunden ausgeführt wird und dem Image-Steuerelement ein neues BitmapImage als Source zuweist:


imageElements[currentImageElement].Source = new BitmapImage(new Uri(System.IO.Path.Combine(mediaDir, imageFiles[currentImageFile])));

Nach kurzer Recherche bedeutet wohl "Nicht genügend Quoten" so viel wie "zu wenig Systemresourcen". Also zu wenig RAM wohl? Das ist aber nicht der Fall. Im laufenden Betrieb genehmigt sich meine Anwendung dauerhaft nur 64Mb RAM. Laut Taskmanager sind auch nur 61% des RAMs belegt, und selbst wenn, dann gäbe es da ja noch die Auslagerungsdatei. Zudem tritt der Fehler nur völlig sporadisch auf. Ich erzeuge da ständig BitmapImage-Objekte, welche ich nie auflöse. Aber der Garbage-Collector sollte sich ja nach einer Weile darum kümmern, die nicht von meinen beiden Image-Steuerelementen gerade geladenen und somit verwaisten BitmapImage-Objekte wieder aus dem RAM zu schmeißen.

Klar, ich könnte jetzt die Exception an entsprechender Stelle adäquat abfangen aber lieber wäre es mir, das Problem an der Wurzel zu packen. Wie kann ich das weiter untersuchen?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Typisches Beispiel, wieso man immer mit einem englischen System als Entwickler unterwegs sein sollte: bescheiden übersetzte Fehlermeldungen.

Ich würde mal aktuell davon ausgehen, dass mit Quoten das Quota gemeint ist.
Also zB. die durch administrative Einschränkung von Speicherplatz auf der Festplatte. Sehr verbreitet auf Terminalservern oder bei Unternehmen.

Der Taskmanager ist keine verlässliche Quelle für den tatsächlich verwendeten Arbeitsspeicher.
Dein StackTrace passt nicht zu Deiner analysierten Code-Stelle. Im StackTrace ist ein eindeutiges Schreiben eines Streams zu sehen zusammen mit einem "maybeFullpath".
Spricht also für die Quota-Situation, also dass Dir irgendwo der verfügbare Festplattenspeicher ausgeht.

Hast Dir denn mal die InnerException angeschaut?
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Danke. Nun sind meine 3 Tablets, auf denen die Software gerade läuft, ja ganz normale Consumer-Geräte mit frisch installiertem Windows 10 Home und haben alle drei noch mehrere 100mb Festplattenspeicher übrig
Zitat
Dein StackTrace passt nicht zu Deiner analysierten Code-Stelle. Im StackTrace ist ein eindeutiges Schreiben eines Streams zu sehen zusammen mit einem "maybeFullpath".
Spricht also für die Quota-Situation, also dass Dir irgendwo der verfügbare Festplattenspeicher ausgeht.

Hm ja das sehe ich. Ich dachte nur, dass
"bei P_Slideshow_Vorabversion.MainWindow.<ImageChangeTimer_Tick>d__24.MoveNext()"
impliziert, dass es im Timer-Tick Event auftritt? Ist das nicht so?

Darüberhinaus gibt es in meinem gesamten Code auch keine Stelle, an der einfach so ein Schreibzugriff erfolgt. Und meine Logging-Funktion wird erst dann aufgerufen, nachdem die Exception schon geworfen wurde, um diese zu loggen, was ja auch funktioniert.

Die InnerException habe ich noch nicht untersucht. Ich muss jetzt erst mal abwarten, bis der Fehler wieder auftritt.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5985
Herkunft: Leipzig

beantworten | zitieren | melden

Hi PoWl,
Zitat von PoWl
Ich erzeuge da ständig BitmapImage-Objekte, welche ich nie auflöse. Aber der Garbage-Collector sollte sich ja nach einer Weile darum kümmern, die nicht von meinen beiden Image-Steuerelementen gerade geladenen und somit verwaisten BitmapImage-Objekte wieder aus dem RAM zu schmeißen.

Das ist keine gute Idee. Um die Freigabe von nicht verwalteten Resourcen wie Bitmaps mußt du dich selbst kümmern, z.B. mit der IDisposible.Dispose-Methode oder mittels using.
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Hm sehr gerne, aber der Garbage-Collector kommt damit scheinbar ganz gut zurecht. Immerhin bläht sich meine Anwendung ja über die Stunden, die sie läuft, nicht immer weiter auf. Das ist natürlich nicht besonders edel aber es schien bisher auch nicht weiter zu stören. Aber was ist, wenn ich das BitmapImage-Objekt, nachdem ich es dem Image-Steuerelement als Source zugewiesen habe, direkt wieder zerstöre? Verschwindet dann das Image? oder kann ich das einfach so tun?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5985
Herkunft: Leipzig

beantworten | zitieren | melden

Der GarbageCollector kümmert sich nicht um nicht-verwaltete Resourcen, deshalb heißen sie nicht-verwaltet: Cleaning Up Unmanaged Resources.

Und nein, natürlich kannst du ein Objekt nicht mehr verwenden, nachdem du den Speicher dafür freigegeben hast.
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Warum stellt ein Bitmap eine nicht-verwaltete Ressource dar? BitmapImage stellt keine Dispose() Methode bereit.

Ich habe zwar folgenden Codefetzen entdeckt:

                    var bitmap = new BitmapImage();
                    var stream = File.OpenRead(System.IO.Path.Combine(mediaDir, imageFiles[currentImageFile]));
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.StreamSource = stream;
                    bitmap.EndInit();
                    stream.Close();
                    stream.Dispose();

Aber der ändert ja nichts daran, dass das BitmapImage-Objekt im Speicher bleibt. Ich sehe aber auch nach wie vor keine Notwendigkeit, mich darum explizit zu kümmern, denn ich habe ja kein Memory-Leak. Die meisten Bilddateien, welche ich zuvor im Programm geladen hatte, kann ich auch während das Programm noch läuft, im Windows Explorer löschen. D.h. die sind nicht durch mein Programm blockiert.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Deine Argumentation stimmt so nicht, PoWl.
Es gibt nicht nur eine beschränkte Anzahl von RAM-Ressourcen, die Du verwenden kannst, sondern auch Windows Handles.
Jede Allokierung eines Bildes nimmt auch ein Handle in Anspruch. Gibst Du dieses nicht frei, dann wird das Handle auch nicht freigegeben.
Kommst Du an das Limit der verfügbaren Handles, dann knallts - wie zB. es hier der Fall sein kann.

Ich bin kein WPF Profi, aber wenn man sich die MSDN so durchliest, dann muss man auch ein BitmapImage wieder aktiv freigeben; ansonsten gibts Memory Leaks und/oder offene Handles.
Dass Du nicht die Notwendigkeit siehst, dass Du Dich drum kümmern müsst - nimm mir das jetz nich übel, würde ich jetzt direktweise darauf zurückführen, dass Du nicht weisst, was Du da im Hintergrund wirklich tust :-)

Nur weil Du etwas im Explorer löschen kannst heisst das nicht, dass Du nicht ein Handle drauf und damit ein Leak hast.

PS: Deine Fehlermeldung auf Englisch lautet
Fehler
Not enough quota is available to process this command
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Hm ok! :-)
Kann ich mir die verwendeten Handles irgendwo angucken? Die müssten ja dann im Laufe der Zeit kontinuierlich steigen.

Ist der Codefetzen, so wie ich ihn nun gezeigt habe, ausreichend?

Wie gesagt, das BitmapImage stellt kein Dispose() bereit. Ich weiß nun nicht, wie ich mich darum kümmern kann.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Alles, was Dispose implementiert (zB streams), sollte man mit Hilfe eines using() nutzen.
Das zeigt auch, dass Du Dir den Unterschied von Close() und Dispose() bei den Stream-Klassen mal anschauen solltest....

Und dass Du das nun fragst zeigt auch, dass Du Dir meinen verwiesenen Link nicht angeschaut hast, und damit auch offensichtlich nicht über Freeze() gestolpert bist....
Keine 2 Minuten später kam Deine Antwort. Du kannst Dir also den Link gar nicht angeschaut haben; vermutlich haste Dir nicht mal meinen Beitrag ganz durchgelesen.
Da frag ich mich dann schon, wieso ich mir die Mühe mach, Dir den Link raus zu suchen...
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Ja danke =), den Link kannte ich bereits deswegen war ich so schnell. Du meinstest wohl auch StackOverflow und nicht MSDN?

Die Freeze-Methode habe ich mittlerweile (ohne dies bereits erwähnt zu haben) auch implementiert. Allerdings würde ich wirklich gerne mal nachprüfen, ob ich irgendwelche Ressourcenleaks habe oder nicht. Ich möchte ja nicht nur aus Angst alle möglichen Codeteile einbauen, ohne dass diese tatsächlich notwendig wären. Ich möchte verstehen, was ich da tue. Ich weiß nur, dass es alle paar Tage mal eine Exception beim laden des Bildes gibt.

Mittlerweile sieht das ganze nun so aus:

var bitmap = new BitmapImage();
using (var stream = File.OpenRead(System.IO.Path.Combine(mediaDir, imageFiles[currentImageFile])))
{
    bitmap.BeginInit();
    bitmap.CacheOption = BitmapCacheOption.OnLoad;
    bitmap.StreamSource = stream;
    bitmap.EndInit();
    bitmap.Freeze();
    stream.Close();
}

imageElements[currentImageElement].Source = bitmap;

1) bitmap.CacheOption = BitmapCacheOption.OnLoad;
Was bewirkt das? "Caches the entire image into memory at load time. All requests for image data are filled from the memory store.", das Bild wird also aus der Bilddatei geladen und im Arbeitsspeicher gecached. Aber wird es das nicht sowieso? Bei BitmapCacheOption.Default steht "Caches the entire image into memory. This is the default value.". Wo ist der unterschied?

2) Muss ich unbedingt einen FileStream verwenden, welchen ich hinterher mit Dispose() wieder vernichten kann, um das Bild zu laden? Ich könnte das Bild auch per bitmap.UriSource laden. So wie ich das verstehe hilft das nur, um die Datei gleich wieder freizugeben aber erzeugt keinen Leak. Oder irre ich mich?

3) Bbitmap.Freeze()
Ich habe schon mehrfach gelesen, dass es Memory Leaks gibt, sobald ich das BitmapImage, welches ich dem Image zuweise, nicht freeze. Aber ich kann auch nach Stunden an Laufzeit kein MemoryLeak feststellen. Bisher habe ich gelesen, dass Freeze() das von Freezable abgeleitete BitmapImage so tagged, dass es nicht mehr verändert werden kann. D.h. WPF muss es nicht kopieren, um es anzuzeigen, sondern kann es direkt aus dem Speicher verwenden. Aber wenn es nicht freezed wäre, und WPF es kopieren würde, warum sollte das dann zu irgendwelchen Leaks führen?

Ich verstehe die Zusammenhänge nicht.

Warum genau werden die von BitmapImage verwendeten Ressourcen nicht mehr freigegeben? Das sind doch alles managed Objekte? Warum gibt es kein Dispose()?
Dieser Beitrag wurde 8 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Der Stream ist kein active managed object.
Du musst hier also explizit dem GC sagen, wenn Du den Stream nicht mehr brauchst. Steams an für sich verwenden eben Windows Handles, die keine managed objects darstellen.

Dein BitmapImage ist zwar managed, aber will eben die StreamSource haben. Da BitmapImage für Dich nicht den Stream verwaltet, musst Du ihn verwalten.
In der Regel ist das bei allen Objekten so, die den Stream nur via Property und nicht via Konstruktor entgegen nehmen.

Wenn man sich nun 5 Minuten Zeit nimmt und in den Quellcode von EndInit schaut, dann sieht man, dass dabei FinalizeCreation aufgerufen wird.

Es erstellt sich also selbst ein BitmapImage aus Deinem Stream, das es dann auch selbstständig verwalten kann.
BitmapImage arbeitet dabei, wie man sehen kann, mit einer BitmapSource, das das Bild als PixelArray vorhält und dabei keine unmanaged reference hat und somit auch nichts zum disposen hat.
Daher gibt es keine Dispose-Methode.
Entsprechend bist Du eben für den Stream verantwortlich. Gibst Du ihn explizit nicht frei, dann kommst Du irgendwann im Laufzeit der Applikation an die Grenze der verfügbaren Handles -> Big Bang.

Wenn Du es also verstehen willst, dann schau Dir ab und zu auch mal den Quellcode von .NET an, wenn die Doku nicht ausreicht.
Inwiefern die Options hier eine Relevanz spielen weiß ich nicht. Müsste ich jetzt auch wieder in den Quellcode schauen, ob das überhaupt an der entsprechenden Stelle abgefragt wird; aber da Du nun weisst, wie man in den Quellcode schaust, kannste das ja auch selbst machen ;-)
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Das ist ja interessant, ich wusste gar nicht, dass man so ohne weiteres auf den Quellcode Zugriff hat. Wir kommen hier aber ein wenig vom Thema ab. An keiner Stelle meines Programms habe ich jemals einen Stream erzeugt, ohne diesen wieder freizugeben.
Zitat
Dein BitmapImage ist zwar managed, aber will eben die StreamSource haben.
Ok. jetzt gibt es ja wie gesagt auch die Möglichkeit, dem BitmapImage keine StreamSource sondern eine UriSource (im Konstruktor) zu übergeben, so wie ich das anfangs gemacht habe. Damit müsste sich ja das BitmapImage darum kümmern, sich die Daten aus der Datei zu holen und die entsprechenden Ressourcen wieder freizugeben. Das folgt allein schon als logischer Schluss aus der Tatsache, dass ich überhaupt keine Möglichkeit habe, mich von Außen darum zu kümmern, also wird BitmapImage an dieser Stelle selber die "Drecksarbeit" machen.

Ich sehe in der BitmapImage-Klasse folgendes:


        public BitmapImage(Uri uriSource) : this(uriSource, null)
        {
           
        }

       public BitmapImage(Uri uriSource, RequestCachePolicy uriCachePolicy) : base(true)
        {
            if (uriSource == null)
            {
                throw new ArgumentNullException("uriSource");
            }
 
            BeginInit();
            UriSource = uriSource;
            UriCachePolicy = uriCachePolicy;
            EndInit();
        }

d.h. der Einzeiler (der Code, den ich anfangs verwendete, mit dem die Exception irgendwann auftrat)

var bitmap = new BitmapImage(new Uri(System.IO.Path.Combine(mediaDir, imageFiles[currentImageFile])));
und

var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(System.IO.Path.Combine(mediaDir, imageFiles[currentImageFile]));
bitmap.EndInit();

sind völlig äquivalent.

Unter Windows 10 habe ich im Taskmanager leider nicht mehr die Option, mir die von einem Prozess verwendeten Handles anzuschauen. Wohl aber unter Windows 7. Und dort bleibt die Anzahl an Handles die ganze Zeit über im Mittel konstant. Egal in welcher Art und Weise ich das BitmapImage erzeuge und egal ob ich Freeze() einsetze oder nicht. Ich habe mein Programm eben extra mal so modifiziert, damit ich alle bisher vorgestellten Codevarianten per Checkbox einzeln aktivieren und deaktivieren kann. Mit keiner Variante geraten die Handles oder der RAM außer Kontrolle. Selbst mit dem Einzeiler nicht.
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Der Taskmanager ist keine verlässliche Quelle bei .NET Anwendungen für Arbeitsspeicher und Handles.
Das liegt an der Art und Weise, wie .NET mit diesen Ressourcen im Allgemeinen umgeht: How much memory does my .NET application use?
Entsprechend sei hier die Working Sets und Heap erwähnt.

Verwende entsprechend dafür vorgesehene Profiler.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Na dann schaue ich mir mal so einen Profiler an.

Ich verstehe jedoch nicht: Wenn mein Programm ein Handle-Leak haben soll. Warum steigt dann die gesamte Anzahl an Handles im System nicht irgendwann deutlich an? Weder die Prozess-Handles noch die im gesamten System verwendeten Handles verändern sich über einen größeren Zeitraum signifikant. Der Prozess belegt laut Task-Manager immer nur um die 912 Handles. Angenommen es werden im Laufe der Stunden hunderte Handles belegt aber nie freigegeben, müsste sich das doch irgendwo bemerkbar machen, auch ohne Profiler? Wenn ich alle 3 Sekunden ein neues Bild lade müssten das ja 28800 Handles am Tag sein. Das ist mehr als mein System insgesamt gerade anzeigt, das würde schnell auffallen.
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Du bringst das durcheinander.

Die Exception, die Du mittlerweile auf Englisch hast, besagt, dass es was mit den Quotas sind.
Dein falscher Umgang mit Ressourcen ist das andere Thema.

Jetzt kann es natürlich sein, dass das einfach nur ein Folgefehler ist. Aber wir haben das System nicht vor uns, sondern nur Du.
Daher bleibt uns an dieser Stelle nichts anderes als Vermutung zu erstellen und Schlüsse aus den vorhandenen Informationen hier zu ziehen.
Es kann auch was andres als die Handles sein, zB. eben die Quotas.

Haste Dir überhaupt die Mühe gemacht die englische Fehlermeldung im Zusammenhang mit WPF zu googlen?
Scheint keine Unbekannte zu sein. Und Bilder, Ressourcen und Handles scheinen hier eine Rolle zu spielen.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

;(

Na gut, back to topic:
Zitat
not enough quota is available to process this command

Dass das die besagte Fehlermeldung ist, war mir schon von Anfang an klar, obgleich du so freundlich warst, sie irgendwann hier noch mal zu übersetzen. Dementsprechend konnte ich dazu ja auch ein wenig googeln.

MSDN meint dazu:
Zitat
Close some applications and try again. If you still get this message, choose System from Control Panel, then choose Virtual Memory and increase the size of your paging file.

Das spricht nun nicht gerade dafür, dass mein Festplattenspeicher begrenzt ist, sondern dass der Arbeitsspeicher ausgeht und man deshalb die Auslagerungsdatei vergrößern soll. Jedoch ist weder mein Festplattenspeicher zu knapp noch habe ich zu wenig Arbeitsspeicher frei. (Laut Task-Manager liegt die Belegung des Arbeitsspeichers bei 70% und die Auslagerungsdatei ist dynamisch. Festplattenspeicher noch >1Gb)

http://stackoverflow.com/questions/12584619/mysterious-not-enough-quota-is-available-to-process-this-command-in-winrt-port
Hier trat die gleiche Fehlermeldung bei jemandem auf, der offenbar zu viele Änderungen im Visual Tree gleichzeitig ausgeführt hat, was auf mich ja nicht zutrifft, aber weiterhin impliziert, dass der Fehler nicht zwangsläufig etwas mit dem freien Festplattenspeicher zu tun haben muss.
Zitat
Instead, it was about limitations in the Windows messaging system. Apparently it is a little like a stack overflow exception, in that when you make too many changes to the visual tree all at once, [...]

Nach weiterer Recherche ergibt sich: Quotas können alle möglichen Beschränkungen sein.
Zitat
[...] it sets an upper limit to the counted number of resources of a certain type. Common examples are 10,000 windows, 10,000 GDI objects, 10,000 handles.


Ich werde das ganze nun einfach mal laufen lassen und weiterhin warten, wann und wo die Fehlermeldung das nächste mal auftritt. Dieses mal logge ich auch die InnerExceptions mit.




Zum Ressourcenthema:

Am Anfang sagte MrSparkle:
Zitat von MrSparkle
Um die Freigabe von nicht verwalteten Resourcen wie Bitmaps mußt du dich selbst kümmern, z.B. mit der IDisposible.Dispose-Methode oder mittels using.
Das aber funktioniert nicht, da das BitmapImage keine Dispose-Methode zur Verfügung stellt. Da ich keine Möglichkeit dazu habe muss ich davon ausgehen, dass .net sich hier selbst drum kümmert.

Weiterhin sagtest du:
Zitat von Abt
Jede Allokierung eines Bildes nimmt auch ein Handle in Anspruch. Gibst Du dieses nicht frei, dann wird das Handle auch nicht freigegeben.
Ich kann jedoch die Anzahl der benutzten Handles nirgendwo ansteigen sehen. Obgleich .net gleich von Anfang an den Prozess mit hunderten Handles startet, von denen möglicherweise ein Großteil gar nichts mit meinem Programm zu tun hat, so müssten die abertausende Handles, die mein Programm nach einiger Zeit in Anspruch nimmt, ja irgendwo sichtbar sein. Ich kann also keinen Leak feststellen, lediglich vermuten. Aber wie gesagt, habe ich darauf ja auch gar keinen Einfluss. Und dabei darf es, wie ich bereits erörtert habe, keine Rolle spielen, ob ich das Bild per Streamreader einlese (und den Stream wieder manuell freigebe) oder ob ich dem BitmapImage-Objekt einfach eine Uri übergebe, und es die Arbeit selbst machen lasse. Darauf muss ich mich ja verlassen können, sonst ergäbe das UriSource-Property überhaupt keinen Sinn, wenn es ein Ressourcenleak erzeugen würde.

Dein Link führt indirekt übrigens zu folgendem weiterführendem Thema:
http://stackoverflow.com/questions/11202807/garbage-collection-fails-to-reclaim-bitmapimage
Zitat
There was a bug in Wpf that we were bitten by where BitmapImage objects are not released unless you freeze them. http://blogs.msdn.com/b/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx was the original page where we discovered the issue. It should have been fixed in Wpf 3.5 sp1 but we were still seeing it in some situations. Try changing your code like this to see if that is the problem:

WPF 3.5 ist jedoch Schnee von Gestern. Nun behauptet der gute Mensch da, das Problem tritt immer noch, also empfiehlt es sich, weiterhin Freeze() zu nutzen. Jedoch ging es dort die ganze Zeit um Memory Leaks. Nicht um Handles oder sonstiges. Und ich kann wie gesagt kein Memory Leak beobachten. Der Prozess bläht sich nicht auf, die RAM-Auslastung des gesamten Systems bleibt konstant.


Edit: Missverständliche Passage entfernt.
Dieser Beitrag wurde 10 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

.. da fällt einem nicht mehr viel ein.
Zitat
Das aber funktioniert nicht, da das BitmapImage keine Dispose-Methode zur Verfügung stellt.

auf den Satz
Zitat
Um die Freigabe von nicht verwalteten Resourcen wie Bitmaps mußt du dich selbst kümmern, z.B. mit der IDisposible.Dispose-Methode oder mittels using.

Bitmap != BitmapImage.
Hier ging es um die prinzipielle Freigabe von unverwaltetet Ressourcen, wie eben es bei Bitmaps oder Streams der Fall ist.
Dein Code hat(te) hier ein Leck und bei dem Aussageinhalt bleibe ich auch.

Aber ich hab ehrlich gesagt wenig Verständnis dafür, wenn man lieber Vermutung aufsteht, statt Konsequent den Ursachen nachgeht.
In .NET gibt es nun mal Fehlermeldungen, die nicht 1:1 auch die Ursache sind. Beispiel: wird eine OutOfMemoryException auch geworfen, wenn Du noch Terabyte-Weise freien Arbeitsspeicher in Deinem so genauen Taskmanager siehst; einfach weil diese Exception eben nicht nur von unzureichendem Arbeitsspeicher ausgelöst wird, sondern zB. eben auch von unzureichenden Windows Handles. Die Exception heisst trotzdem OutOfMemoryException und nicht OutOfHandlesException. Nichts anderes kann es bei dieser DispatcherException der Fall sein. Sie beschreibt schließlich die Quota, was alles mögliche sein kann, Speicher, Handles..
Du kannst jetzt mit dieser Tatsache leben, sie akzeptieren und mit ihr arbeiten - oder so weiter machen :-)

Ich bin jedenfalls raus hier.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Pardon, vielleicht habe ich mich falsch ausgedrückt: Ich habe lediglich das Gefühl, das Ressourcenproblem ist ein Gespenst, da es (bis auf die Exception) keine sichtbaren Anzeichen gibt. Ich wundere mich lediglich. Denk dran, ich bin ein Neuling, man vergebe mir. Einen Profiler bin ich ja auch gerade am suchen, jedoch muss ich mich da auch erst mal einarbeiten. Vielleicht liefert der mir ein paar interessante Eindrücke.
Zitat
Dein Code hat(te) hier ein Leck und bei dem Aussageinhalt bleibe ich auch.

Du hast deutlich mehr Erfahrung, daher glaube ich dir das nur allzu gerne! Ich kann jedoch lediglich nicht nachvollziehen, warum das denn so ist. Ich umreiße noch mal kurz: Warum entsteht ein Leck, wenn ich das Bild über das Setzten einer UriSource lade (so wie anfangs geschehen), und warum entsteht keines, wenn ich es über die StreamSource lade (und danach den Stream manuell freigebe). Welche Legitimation hat dann die Existenz des UriSource-Properties, wenn dieses zwangsläufig ein Leck erzeugt? Oder meintest du, das fehlende Freeze() war die Ursache für das Leck?

Dass der Taskmanager hier keine Verlässlichen Angaben liefert, das glaube ich dir auch gerne. Aber wo ist der Fehler in meiner Erörterung? Hat .net die Macht, tausende Handles zu reservieren, ohne dass das im Taskmanager irgendwas davon auftaucht? Auch bei der Gesamtzahl der im System vorhandenen Handles nicht?
Zitat
Sie beschreibt schließlich die Quota, was alles mögliche sein kann, Speicher, Handles..
Das habe ich ja nun wie gesagt auch bereits herausgefunden Jetzt kann ich wohl nur noch auf den Profiler setzen, der mir dann hoffentlich aufzeigen wird, wo das Leck ist. Vielleicht hast du eine Empfehlung?
Dieser Beitrag wurde 26 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

procmon.exe zur Analyse zur Laufzeit als externe Anwendung oder Dein in Visual Studio eingebauter Profiler und/oder JetBrains dotTrace / dotMemory.

So mal ne Anekdote:
Ich hatte in einem Windows Service ewig das Problem, dass ich eine Exception (weiß nicht mehr welche genau) im die Ohren bekommen hab, nach ca. 6-12h Laufzeit.
Grund war jedoch, dass ich durch einen Flüchtigkeitsfehler in einem Task einen EventHandler registriert habe.
Dadurch wurde der Task nie aufgeräumt und die dazugehörigen Objekte auch nicht.
Die Exception hatte damit nichts zutun; hat aber durch Folgefehler ausgelöst.

Ich würde hier also eher nach dem Execptiontyp statt nach der Beschreibung suchen.
Es kann, wie Du schon meintest, auch überhaupt nichts mit den Ressourcen zutun haben und wir haben hier einfach ebenfalls einen weiteren Flüchtigkeitsfehler gefunden.
zB weil Du irgendwas asynchron ausführst ohne es zu locken oder sowas. Einfach Race Conditions, an die man nicht denkt.

Ich vermute, dass dieser Fehler hier im Forum als Zufall gefunden wird; also nach dem Motto "hast Du evtl. das und das?"
"Oh stimmt, da hab ich was, das könnte es sein!".

Nein, .NET hat nicht die Macht, Handles zu erstellen, ohne, dass diese im Task Manager auftauchen.
Im Gegenteil: Handles können nur durch die Win32 API erstellt werden, die Windows zur Verfügung stellt. .NET ist hier nur ein Konsument dieser Windows API.
Die Anzahl der Handles müsste jedoch im 100er- oder gar 1000er-Bereich hin und her springen, was zumindest bei mir und hier aktuell auf meinem Windows Server 2016 auch der Fall ist. Daher ist dabei gar keine verlässliche Quelle zu identifizieren, wer diese Handles (de)registriert und für diese 100er-Schwankung verantwortlich ist.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Ja, die Anzahl der Handles springen in der Tat im 100er-Bereich hin und her. Müsste sich bei einem Handle-Leak nicht aber insgesamt gesehen eine langfristige Tendenz nach Oben abzeichnen?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16109

beantworten | zitieren | melden

Prinzipiell ja, aber nicht immer sofort erkennbar.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

beantworten | zitieren | melden

Hast du dir im Taskmanager auch schon mal die GDI-Objekte angesehen? Ich weiß nicht, ob WPF überhaupt irgendwie GDI benutzt, aber unter Windows Forms könnte das auch ein möglicher Grund sein.
private Nachricht | Beiträge des Benutzers
PottKafe
myCSharp.de - Member



Dabei seit:
Beiträge: 19
Herkunft: NRW

beantworten | zitieren | melden

Wenns ein speicher Problem ist kann es gut sein das du die WPF Bindings nicht richtig implementiert hast.
Für jede klasse auf die gebunden wird, die dennoch nicht 'INotifyPropertyChanged' implementiert wird per Reflection ein PropertyBinding erzeugt welches du nie aus dem Arbeitsspeicher bekommst.
Schau mal nach ob du das 'INotifyPropertyChanged' irgendwo vergessen hast.

Um nachzuschauen Wo & Ob du ein speicher Problem hast kannst du die DiagnosticTools vom Visual Studio verwenden oder Manuell eine Weakreference in einer Liste ablegen & nach einer zeit schauen ob bei den WeakReference's noch IsAlive auf true steht.
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Zitat von MorphieX
Hast du dir im Taskmanager auch schon mal die GDI-Objekte angesehen? Ich weiß nicht, ob WPF überhaupt irgendwie GDI benutzt, aber unter Windows Forms könnte das auch ein möglicher Grund sein.
Also im Taskmanager / Process Monitor werden bei den Handles, GDI-Objekten und Threads über den gesamten Zeitraum eine nahezu konstante Zahl angezeigt. Wie Abt schon gesagt hat kann man sich ja auf diese Angaben leider nicht verlassen (Handles: 910, Threads: 28, GDI-Objekte: 41)
Zitat von PottKafe
Wenns ein speicher Problem ist kann es gut sein das du die WPF Bindings nicht richtig implementiert hast.
Interessant, scheinbar erzeugt man ja an allen Ecken und Stellen in WPF irgendwelche Memory Leaks. Bindings nutze ich aber nicht. Ich habe nur zwei Image-Steuerelemente, die abwechselnd mit neuen Bildern beladen werden und bei dem das vordere Image immer ein- bzw. ausgeblendet wird.

Noch mal eine grundlegende Frage: Müsste sich bei einem Memory-, Handle- oder GDI-Leak nicht eine der Zahlen auch im ungenauen Taskmanager in der Prozess-Ansicht im laufe der Zeit aufblähen? Die verändern sich halt überhaupt nicht. ist der Taskmanager bei .NET-Prozessen nicht nur ein "Schätzeisen" sondern vollkommen komplett nutzlos? Nur damit ich weiß, welches Ausmaß das hat.
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10083

beantworten | zitieren | melden

Zitat
Interessant, scheinbar erzeugt man ja an allen Ecken und Stellen in WPF irgendwelche Memory Leaks. Bindings nutze ich aber nicht. Ich habe nur zwei Image-Steuerelemente, die abwechselnd mit neuen Bildern beladen werden und bei dem das vordere Image immer ein- bzw. ausgeblendet wird.
Und du hast die bilder auch disposed?
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Zitat von FZelle
Und du hast die bilder auch disposed?
Nein, ich weiß leider nicht, wie ich das tun sollte. BitmapImage stellt keine Dispose-Methode bereit. Und das Image-Steuerelement stellt auch nichts bereit, womit das möglich wäre.
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers
Spook
myCSharp.de - Member



Dabei seit:
Beiträge: 240
Herkunft: Esslingen a.N.

beantworten | zitieren | melden

Hallo PoWl,

könntest du bitte den Code der Methode "P_Slideshow_Vorabversion.MainWindow.<ImageChangeTimer_Tick>d__24.MoveNext()" posten. Vielleicht kommen wir damit weiter.

Danke

spooky
private Nachricht | Beiträge des Benutzers
ujr
myCSharp.de - Experte



Dabei seit:
Beiträge: 1770

beantworten | zitieren | melden

Was bisher hier nicht beachtet wurde ist der "Console.WriteLine" Aufruf im Callstack.

Davon wurde hier berichtet:
http://stackoverflow.com/questions/27164538/console-writeline-throws-not-enough-storage-is-available-to-process-this-comman

Leider ohne Lösung. Evtl. ist der dort verlinkte Artikel hilfreich.

Oder der
Not enough storage is available to process this command

Falls hilfreich, solltest Du hier Deine Lösung posten.

Ohne Code lässt sich kaum noch etwas dazu sagen.
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 220

Themenstarter:

beantworten | zitieren | melden

Sehr gerne. Das hier ist der Code der Timer-Tick Routine, so wie er vor ein paar Tagen zum Zeitpunkt des Auftretens des Fehlers aussah:


        async private void ImageChangeTimer_Tick(object sender, EventArgs e)
        {
            Random rnd = new Random();

            lock (locker)
            {
                nextimage:
                do
                {
                    currentImageFile = rnd.Next(0, imageFiles.Count);
                    if ((history.Contains(currentImageFile)))
                        Console.WriteLine("douh! " + currentImageFile);
                }
                while (history.Contains(currentImageFile));
                history.Enqueue(currentImageFile);

                Console.WriteLine(currentImageFile);

                currentImageElement++;
                if (currentImageElement > 1)
                    currentImageElement = 0;

                try
                {
                    // Achtung, das hier wurde mittlerweile durch obengenannten Code geändert!
                    imageElements[currentImageElement].Source = new BitmapImage(new Uri(System.IO.Path.Combine(mediaDir, imageFiles[currentImageFile])));
                }
                catch (FileNotFoundException)
                {
                    log("Error! File not Found: " + imageElements[currentImageElement]);
                    imageFiles.RemoveAt(currentImageElement);
                    history.Clear();
                    goto nextimage;
                }
            }

            await Task.Delay(500); // Animation flüssiger machen

            if (currentImageElement == 1)
                sbFadeIn.Begin(Image2);
            else
                sbFadeOut.Begin(Image2);
        }

Darüberhinaus macht mein Programm nichts. Es ist der einzige Timer der läuft. Andere Programmteile werden nicht aufgerufen. Ich konnte bisher leider den Fehler nicht reproduzieren. Das Programm läuft gerade auf allen 3 Tablets seit 15h durch. Ich lass es mal weiter laufen.

Mittlerweile logge ich auch alle Exceptions mit. Im Internet fand ich, dass AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException; ausnahmslos alles einfängt, was an Exceptions geworfen wird.

        private void CurrentDomain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
        {
            Exception ex = e.Exception;
            log("FirstChanceException: " + ex.ToString());

            while (ex.InnerException != null)
            {
                ex = ex.InnerException;
                log("InnerException: " + ex.ToString());
            }
        }
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von PoWl am .
private Nachricht | Beiträge des Benutzers