0

If I run the following program I see the free memory rapidly decrease to zero in Windows Task manager. Is it forbidden to use NSubstitute in loops?

using System;
using NSubstitute;
using System.Threading;

namespace NSubstituteMemoryLeaks
{
    class Program
    {
        static void Main(string[] args)
        {
            IConfig config = Substitute.For<IConfig>();
            config.Value.Returns(0);

            Thread th = new Thread(() => {
                while (true)
                {
                    int val = config.Value;
                }
            });
            th.IsBackground = true;
            th.Start();

            Console.WriteLine("Press ENTER to stop...");
            Console.ReadLine();
        }
    }

    public interface IConfig
    {
        int Value { get; set; }
    }
}
  • Maybe is a silly question but, what are you trying to do? As some of the guys have explained below, `config.Value` will be receiving the call of you reading that property. – rodrigoelp Sep 24 '18 at 01:14
  • I had the problem in a more complex loop. When I found the source of the problem I wanted to know why it happened – Marco Segurini Sep 24 '18 at 06:49
  • Oh, good. In any case, you can follow either suggestion. If all you need is canned data, you can create the actual implementation and change it accordingly (based on your test) – rodrigoelp Sep 24 '18 at 09:36

2 Answers2

1

Mock generates objects. The problem is the program creates those in a short period of time and doesn't give enough time to Garbage Collector to collect objects.

It is not specific to NSubstitute. You can see the same behavior in Moq too.

You could solve it by explicitly calling GC.Collect();.

Task.Run(() =>
{
    while (true)
    {
        int val = config.Value;
        GC.Collect();
    }
});

There are pro and con. You might want to read When to call GC.Collect() before implementing it.

Win
  • 61,100
  • 13
  • 102
  • 181
  • Hi @Win, I don't think it will work. As David explains below, the garbage collection is not going to collect the entries of the action done to read `config.value`. You will need to clear the received calls to dump that memory and continue on. – rodrigoelp Sep 24 '18 at 01:16
  • @rodrigoelp I tested the above code; memory still flats compare to original code which gradually increase the memory usage. – Win Sep 24 '18 at 02:43
1

NSubstitute records all calls made to a substitute, so if you are calling a substitute in an infinite loop it will eventually exhaust available memory. (If you call config.ReceivedCalls() after 10,000 loop iterations you should see 10,000 entries in that list.)

If you call config.ClearReceivedCalls() periodically in the loop this might help.

If you have a bounded loop this should not be an issue; the memory will be cleared once the substitute is no longer in use and GC cleans it up.

David Tchepak
  • 9,826
  • 2
  • 56
  • 68