reactiveui / ReactiveUI.Validation

Validation helpers for ReactiveUI-based apps.
https://reactiveui.net/docs/handbook/user-input-validation
MIT License
235 stars 24 forks source link

[Bug]: Memory leak when using ReactiveValidation #659

Closed Josch600 closed 4 months ago

Josch600 commented 4 months ago

Describe the bug 🐞

A class derived from ReactiveValidationObject is not garbage collected, if it has set a validation rule and subscribed to ValidationContext.ValidationStatusChange.

Step to reproduce

  1. Create a class:
    
    using System;
    using System.Reactive.Disposables;
    using ReactiveUI.Validation.Extensions;
    using ReactiveUI.Validation.Helpers;

namespace RxValidation { public sealed class Class1 : ReactiveValidationObject, IDisposable { private readonly CompositeDisposable disposable = new CompositeDisposable();

    public Class1()
    {
        this.ValidationRule(
            vmp => vmp.Name,
            name => !string.IsNullOrEmpty(name),
            "The name is empty.");

        // commenting out the following statement makes the test green
        this.ValidationContext.ValidationStatusChange
            .Subscribe(/* you do something here, but this does not matter for now. */)
            .DisposeWith(this.disposable);
    }
    public string Name { get; set; }

    public void Dispose()
    {
        disposable?.Dispose();
    }
}

}

2. Create a test:

using System; using FluentAssertions; using JetBrains.dotMemoryUnit; using RxValidation; using Xunit; using Xunit.Abstractions;

namespace RxValidationTests { public class Class1Tests { public Class1Tests(ITestOutputHelper testOutputHelper) { DotMemoryUnitTestOutput.SetOutputMethod(testOutputHelper.WriteLine); }

    /// <summary>Tests whether the created object can be garbage collected.</summary>
    [Fact]
    [DotMemoryUnit(FailIfRunWithoutSupport = false)]
    public void Instance_Released_IsGarbageCollected()
    {
        WeakReference reference = null;
        new Action(
            () =>
            {
                var sut = new Class1();

                reference = new WeakReference(sut, true);
                sut.Dispose();
            })();

        // Sut should have gone out of scope about now, so the garbage collector can clean it up
        dotMemory.Check(
            memory => memory.GetObjects(
                where => where.Type.Is<Class1>()).ObjectsCount.Should().Be(0, "it is out of scope"));

        GC.Collect();
        GC.WaitForPendingFinalizers();

        reference.Target.Should().BeNull("it is garbage collected");
    }
}

}


3. Run the test either under dotMemoryUnit (the dotMemory.Check will fail) or with normal Testrunner (reference.Target.Should().BeNull will fail)

Here is the complete code: 
[RxValidation.zip](https://github.com/reactiveui/ReactiveUI/files/15090288/RxValidation.zip)

### Reproduction repository

none

### Expected behavior

The test should run green, because the instance of Class1 is released from memory.

### Screenshots 🖼️

_No response_

### IDE

Visual Studio 2019

### Operating system

Windows 10 Enterprise

### Version

22H2

### Device

PC

### ReactiveUI Version

19.6.1

### Additional information ℹ️

The problem was detected in version 19.2.1 and verified in version 19.6.1
ChrisPulman commented 4 months ago

@Josch600 Thank you for raising this issue, I believe that this will be fixed in V4.0.9 if you still have issues with the memory, please raise a new issue.

github-actions[bot] commented 3 months ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.