开发者

Which objects does the garbage collector not clean up?

开发者 https://www.devze.com 2023-01-26 20:10 出处:网络
A static开发者_开发技巧 analysis tool keeps telling me that my C# code has resource leaks in it.

A static开发者_开发技巧 analysis tool keeps telling me that my C# code has resource leaks in it.

Here's an example:

StringReader reader = new StringReader(...); 

// do something with reader

...

} // static analysis tool cries that I've leaked **reader**

Is my tool correct? If so, why?

Edit (replying to comment) - My static analysis tool says that I've got a bunch of resource leaks. I know from this forum that certain Java AWT objects need to be explicitly freed, otherwise a leak occurs. Are there C# objects that need to be explicitly freed?


Yeah, your code is leaking badly. It should be like this:

using (StringReader reader = new StringReader(...))
{

}

Every class that implements IDisposable needs to be wrapped in a using block to ensure that the Dispose method is always called.


UPDATE:

Elaborating: in .NET there's the IDisposable interface which defines the Dispose method. Classes that implement this interface (like files streams, database connections, readers, ...) might contain pointers to unmanaged resources and the only way to ensure that those unmanaged resources/handles are freed is to call the Dispose method. So in .NET to ensure that some code is called even if an exception is thrown is to use a try/finally statement:

var myRes = new MyResource(); // where MyResource implements IDisposable
try
{
    myRes.DoSomething(); // this might throw an exception
}
finally
{
    if (myRes != null)
    {
        ((IDisposable)myRes).Dispose();
    }
}

People writing C# code have quickly realized that writing this every time you was dealing with a disposable resource was a PITA. So they've introduced the using statement:

using (var myRes = new MyResource())
{
    myRes.DoSomething(); // this might throw an exception
}

which is kinda shorter.


Your code isn't actually leaking anything in this specific case, because StringReader doesn't really have any resources to clean up, just like MemoryStream doesn't. (With MemoryStream you can still end up needing to dispose of it if you're using it asynchronously or remoting it... but in straightforward cases it doesn't matter.)

However, it's a good idea to dispose of anything which implements IDisposable just on principle. This will avoid you leaking (perhaps temporarily) unmanaged resources such as file handles.

For example, suppose you changed your code to:

StreamReader reader = new StreamReader("file.txt");
...

If you don't close or dispose reader here (in a finally block or via a using statement) it will hold the file open until whatever type is directly holding the OS file handle is finalized. Disposing of things explicitly not only frees unmanaged resources earlier - it also takes finalizable objects off the finalization queue, which means they can be garbage collected earlier.


It looks like you are not disposing of your StringReader. You need to call .Dispose() to clean up unmanaged resources. Or better yet, use this in a using block as such:

using (StringReader reader = new StringReader(...))
{
    // your code
}

This will cause Dispose() automatically at the end of the block, even if your code throws an exception (identical to using a finally block).


As others have said, it boils down to the StringReader not being disposed, so I won't hit on that.

What's going on is that the static analysis tool is essentially a dumb tool. And I don't mean dumb as in don't use it, I mean dumb as in it is looking at a very limited criteria.

In this case it sees an object being instantiated whose class implements IDisposable. The tool is then simply looking to see if you make a corresponding dispose call before the object goes out of scope. This can be made by either explicitly saying object.Dispose(); or through the using(var x = ...) { } clause.

According to MS specs, classes should implement IDisposable in the event they deal with unmanaged resources (like file handles). Now, you might want to review this MSDN post which talks about which classes that implement IDisposable that you don't necessarily have to call dispose() on.

Which leaves us with two viable solutions. The first (and one that I and Darin recommend) is to always wrap an object that implements IDisposable in a using clause. It's just good practice. After all, it causes zero harm to have in place, whereas not having it could cause lots of memory leaks (depending on the class) and I'm not smart enough to remember which is which.

The other would be to configure your static analysis tool (if possible) to ignore such warnings. I really think this would be a Bad Idea (tm)


There are many classes of streams, with associated types of reader objects. Some of those classes manipulate outside objects in a way which must be undone before they are completely abandoned, but can't be undone while they are still needed (e.g. a file reader will open a file; the file must be closed before the file handle is forgotten, but can't be closed until the reader is done with it). The "Dispose" method will take care of anything that must be "put back" by the reader before it's completely abandoned. Because it is common for one piece of code to create a stream reader and hand it off to another piece of code, and because the first piece of code may have no way of knowing when the code using the reader is done with it, the code using the reader has the responsibility of calling Dispose on the reader when it's done with it.

Note that some types of readers don't actually do anything in their Dispose method, but any code which accepts arbitrary types of stream readers should call it. It's possible that while your code was expects a type of stream reader that does not require dispose, it may be given a derived class which does require it. Calling Dispose even when not strictly necessary will protect against such scenarios.


I believe it's because you haven't called the Close method after you've finished with it, though according to MSDN:

This implementation of Close calls the Dispose method passing a true value.

So I would expect that when the garbage collector gets around to the reader, it will be Disposed with the same end result (and no memory leak).

UPDATE: I am mistaken, the GC does not automatically Dispose IDisposable objects. You need to call Close (or Dispose) explicitly.


you have leaked, but 'eventually' the GC will clean it up for you.


A stringreader could be accessing a stream. By not disposing of the stringreader you could potentially leave that stream open. The stream could be attached to a file on the system and in turn you might have locked it.

try looking into the using statement and that will call dispose for you automatically.

using (sr) {
 // your code
}


The garbage collector will collect anything that no longer has a reference to it. In your sample, reader will be collected eventually (although, no one can tell when).

However, the 'static analysis tool' is complaining that you aren't manually calling Dispose().

StringReader reader = ...
...
reader.Dispose();

In this specific case, it's probably not a big deal. However, when dealing with many IO classes (*Stream, *Reader, etc), it's good to dispose of them when you're done. You can use using to help:

using(StringReader reader = ...) {
    ...
}  //reader is automatically disposed here
0

精彩评论

暂无评论...
验证码 换一张
取 消