2013-05-21 19 views
6

Ich habe festgestellt, dass in meiner .NET 3.5-Anwendung, die ReactiveUI verwendet, habe ich einen erheblichen Speicherverlust, der von ObservableAsPropertyHelper stammt. Ich habe ein Testprojekt erstellt, um es zu demonstrieren here.ReactiveUI ObservableAsPropertyHelper/Reaktive Erweiterungen Speicherleck?

Es scheint, dass jede Änderungsbenachrichtigung, die von einer einfachen berechneten ObservableAsPropertyHelper-Eigenschaft ausgelöst wird, Speicher verliert. Das Leck scheint in Reactive Extensions zu entstehen, nicht direkt in ReactiveUI, aber meine Verwendung von OAPH ist so einfach, dass ich mich frage, ob irgendjemand auf dieses Problem gestoßen ist oder einen vorgeschlagenen Fix hat.

Der Schweregrad des Speicherlecks variiert zwischen .NET 3.5 (RxUI 2.4, Rx 1.1) und .NET 4.0 (RxUI 4.2, Rx 2.0.3). Es ist viel näher an linear mit jeder Aktualisierung der Eigenschaft in .NET 3.5. Das Leck ist jedoch immer noch in .NET 4.0 vorhanden.

Ich habe das Testprojekt und einige Profiler-Images für meine .NET 3.5- und .NET 4.0-Testsession mit der Testanwendung here hochgeladen.

Sie können auf den Bildern sehen, dass die Objektdiagramme unterschiedlich sind, so dass wir möglicherweise über zwei verschiedene Lecks sprechen. In der 4.0-Sitzung (40_RetentionGraph.png) können Sie sehen, dass die am häufigsten zugeordneten Objekte Ints (der Typ meiner OAPH-Eigenschaft) und ConcurrentQueue sind. Es scheint eine Art zirkuläres Referenzproblem zu geben. Sie können auch in 40_IntsAllocatedGCRootGrows.png sehen, dass der Abstand vom GC-Root der Instanzen zunimmt.

In der Version 3.5 (worüber ich am meisten besorgt bin) können Sie sehen (35_Summary.png), dass die am meisten zugewiesenen Objekte Action und ScheduledObserver sind. Der Objektgraph ist etwas komplexer und insgesamt anders als die 40er Version.

Ich habe this discussion überprüft aber habe keine direkte Antwort gefunden: mein Szenario ist, sehr einfache Updates zu OAPH. Jeder Einblick in mögliche Lösungen für dieses Leck wird geschätzt.

Antwort

4

Sie haben keinen Scheduler angegeben, sodass RxUI standardmäßig den DispatcherScheduler verwendet. Da es sich bei Ihrer App um eine Konsolenanwendung handelt, wird kein Dispatcher ausgeführt. All diese Speicher werden vom OnNext-System verbraucht, in dem nichts läuft, um sie auszuführen.

Sie könnten Chaos über mit dem Brennen einen Dispatcher und Fütterung es zu laufen Rahmen (die eine WPF-Anwendung für Sie tun würde), oder zum Zweck der Prüfung nur den ReactiveTester Konstruktor von diesem ändern:

public ReactiveTester() 
{ 
    _Total = this.WhenAny(x => x.A, x => x.B, (a, b) => a.Value + b.Value) 
       .ToProperty(this, x => x.Total); 
} 

Um dies:

public ReactiveTester() 
{ 
    _Total = this.WhenAny(x => x.A, x => x.B, (a, b) => a.Value + b.Value) 
       .ToProperty(this, x => x.Total, 0, Scheduler.CurrentThread); 
} 

Und alles wird gerade rosig sein.

+0

Ja, ich vermutete dies in dem RxUI-Fehler, der abgelegt wird. Gute Arbeit. –

+0

Sie haben Recht, das Hinzufügen des Problems löst das Problem in der Test-App. Danke James und Paul! Ich vermute, meine Frage ist: Was könnte ein ähnliches Leck in meiner WPF-App verursachen? Meine .WhenAny() werden sowohl von der Benutzeroberfläche als auch von Hintergrundthreads aufgerufen. Muss ich bei jedem Aufruf von .ToProperty() explizit einen Scheduler angeben? Wenn ja, sollte es Scheduler.CurrentThread sein? –

+1

Ich spekuliere, ohne Ihren Code zu sehen, aber ich sehe, dass Ihr 3.5-Beispiel RxUI und Rx 1.1 verwendet. IIRC, diese Kombination führt dazu, dass ein neuer Dispatcher erstellt wird (der nicht abgepumpt wird), wenn Sie ToProperty() in einem Nicht-UI-Thread aufrufen und keinen Scheduler angeben. Versuchen Sie, Ihren Initialisierungscode auf dem Dispatcher auszuführen. –