-1

Let's say we have the following code:

class Program {
   static void Main(string[] args) {
      FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
      StreamWriter sw = new StreamWriter(fs);
      sw.Write("Hi there");
                          // <-----doesn't call sw.Dispose()
      GC.Collect();       // not necessary to force GC run as MDAs should break anyway, but still call it to be safe and hopefully can throw an run time exception
      Console.ReadLine();
   }
}

Notice that the StreamWriter’s constructor takes a reference to a Stream object as a parameter, allowing a reference to a FileStream object to be passed as an argument. Internally, the StreamWriter object saves the Stream’s reference. When you write to a StreamWriter object, it internally buffers the data in its own memory buffer. When the buffer is full, the StreamWriter object writes the data to the Stream.

We know that we supposed to call sw.Dispose();, becuase if the FileStream object were finalized first, it would close the file. Then when the StreamWriter object was finalized, it would attempt to write data to the closed file, throwing an exception.

So I tick on StreamWriterBufferredDataLost checkbox of Managed Debugging Assistants (MDAs) in visual studio to make the program breaks when it detect Dispose is not explicitly called.

But when I run my code, it doesn't break, MDAs doesn't pop up, what's going on?

amjad
  • 3,048
  • 1
  • 11
  • 42
  • 1
    GC.Collect() does not collect anything. Insert `sw = null;` before the collect call and you'll now see the MDA step in. Backgrounder for this behavior [is here](https://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net). – Hans Passant Apr 11 '21 at 09:13
  • @HansPassant thanks for your anser. I have checked your answer on the post, very informative. Just want to confim, does settiing a local variable to null work different between debug mode and release mode? I mean in debug mode, `sw = null;` is meaningful while in release mode, JIT compiler optimizes the sw = null; line out of the code completely? – amjad Apr 11 '21 at 14:46

1 Answers1

0

GC.Collect() won't collect your sw because you still have a reference to it!

This code should trigger MDA:

void Test()
{
    FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
    StreamWriter sw = new StreamWriter(fs);
    sw.Write("Hi there");
}

Test();
GC.Collect();

Here sw is out of scope when you call GC.Collect(), thus it will be collected, triggering the MDA

torvin
  • 5,713
  • 1
  • 34
  • 51
  • I am running the program in debug mode, which has some optimization features. When GC examines the application’s roots and sees that Main doesn’t use the sw variable after executing the Write . Therefore, the application has no variable referring to the sw object, and the garbage collection reclaims the memory for it. – amjad Apr 11 '21 at 08:16
  • That's not true and you can easily check it with a weak reference – torvin Jun 07 '21 at 09:56