Why can .NET not have memory leaks?
Ignoring unsafe code, .NET cannot have memory leaks. I've read this endlessly from many experts and I believe it. However, I do not understand why this is so.
I开发者_如何学Got is my understanding that the framework itself is written in C++ and C++ is susceptible to memory leaks.
- Is the underlying framework so well-written, that it absolutely does not have any possibility of internal memory leaks?
- Is there something within the framework's code that self-manages and even cures its own would-be memory leaks?
- Is the answer something else that I haven't considered?
.NET can have memory leaks.
Mostly, people refer to the Garbage Collector, which decides when an object (or whole object cycle) can be gotten rid of. This avoids the classic c and c++ style memory leaks, by which I mean allocating memory and not freeing it later on.
However, many times programmers do not realize that objects still have dangling references and do not get garbage collected, causing a... memory leak.
This is normally the case when events are registered (with +=
) but not unregistered later on, but also when accessing unmanaged code (using pInvokes or objects that use underlying system resources, such as the filesystem or database connections) and not disposing properly of the resources.
There are already some good answers here, but I want to address one additional point. Let's look very carefully again at your specific question:
It is my understanding that the framework itself is written in C++ and C++ is susceptible to memory leaks.
- Is the underlying framework so well-written, that it absolutely does not have any possibility of internal memory leaks?
- Is there something within the framework's code that self-manages and even cures its own would-be memory leaks?
- Is the answer something else that I haven't considered?
The key here is to distinguish between your code and their code. The .Net framework (and Java, Go, python, and other garbage-collected languages) promise that if you rely on their code, your code will not leak memory... at least in the traditional sense. You might find yourself in situations where some objects are not freed as you expect, but these cases are subtly different from traditional memory leaks because the objects are still reachable in your program.
You are confused because you correctly understand that this is not the same thing as saying any program you create can't possibly have a traditional memory leak at all. There could still be a bug in their code that leaks memory.
So now you have to ask yourself: would you rather trust your code, or their code? Keep in mind here that their code is not only tested by the original developers (just like yours, right?), it's also battle-hardened from daily use by thousands (perhaps millions) of other programmers like yourself. Any significant memory leak issues would be among the first things identified and corrected. Again, I'm not saying it's not possible. It's just that it's generally a better idea to trust their code than it is your own... at least in this respect.
Therefore the correct answer here is that it's a variant of your first suggestion:
Is the underlying framework so well-written, that it absolutely does not have any possibility of internal memory leaks?
It's not that there's no possibility, but that it's much safer than managing it yourself. I'm certainly not aware of any known leaks in the framework.
After reviewing Microsoft documentation, specifically "Identifying Memory Leaks in the CLR", Microsoft does make the statement that as long as you are not implementing unsafe code within your application that it is not possible to have a memory leak
Now, they also point out the concept of a perceived memory leak, or as was pointed out in the comments a "resource leak", which is the use of an object that has lingering references and is not disposed of properly. This can happen with IO Objects, DataSets, GUI elements, and the like. They are what I would typically equate to a "memory leak" when working with .NET, but they are not leaks in the traditional sense.
Due to garbage collection, you can't have regular memory leaks (aside from special cases such as unsafe code and P/Invoke). However, you can certainly unintentionally keep a reference alive forever, which effectively leaks memory.
edit
The best example I've seen so far of a genuine leak is the event handler += mistake.
edit
See below for an explanation of the mistake, and of the conditions under which it qualifies as a genuine leak as opposed to an almost-genuine leak.
Here's an example of a memory leak in .NET, which doesn't involve unsafe/pinvoke and doesn't even involve event handlers.
Suppose you're writing a background service that receives a series of messages over a network and processes them. So you create a class to hold them.
class Message
{
public Message(int id, string text) { MessageId = id; Text = text; }
public int MessageId { get; private set; }
public string Text { get; private set; }
}
OK, so far so good. Later on you realize that some requirement in the system could sure be made easier if you had a reference to the previous message available when you do the processing. There could be any number of reasons for wanting this.
So you add a new property...
class Message
{
...
public Message PreviousMessage { get; private set; }
...
}
And you write the code to set it. And, of course, somewhere in the main loop you have to have a variable to keep up with the last message:
Message lastMessageReceived;
Then you discover some days later than your service has bombed, because it has filled up all the available memory with a long chain of obsolete messages.
Here are other memory leaks that this guy found using ANTS .NET Profiler: http://www.simple-talk.com/dotnet/.net-tools/tracing-memory-leaks-in-.net-applications-with-ants-profiler/
I suppose it is possible to write software, e.g. the .NET runtime environment (the CLR), that does not leak memory if one is careful enough. But since Microsoft does issue updates to the .NET framework via Windows Update from time to time, I'm fairly sure that there are occasional bugs even in the CLR.
All software can leak memory.
But as others have already pointed out, there are other kinds of memory leaks. While the garbage collector takes care of "classic" memory leaks, there's still, for example, the problem of freeing so-called unmanaged resources (such as database connections, open files, GUI elements, etc.). That's where the IDisposable
interface comes in.
Also, I've recently come across with a possible leaking of memory in a .NET-COM interop setting. COM components use reference counts to decide when they can be freed. .NET adds yet another reference counting mechanism to this which can be influenced via the static System.Runtime.InteropServices.Marshal
class.
After all, you still need to be careful about resource management, even in a .NET program.
You can absolutely have memory leaks in .NET code. Some objects will, in some cases, root themselves (though these are typically IDisposable
). Failing to call Dispose()
on an object in this case will absolutely cause a real, C/C++ style memory leak with an allocated object that you have no way to reference.
In some cases, certain timer classes can have this behavior, as one example.
Any case where you have an asynchronous operation that may reschedule itself, you have a potential leak. The async op will typically root the callback object, preventing a collection. During execution, the object is rooted by the executing thread, and then the newly-scheduled operation re-roots the object.
Here's some sample code using System.Threading.Timer
.
public class Test
{
static public int Main(string[] args)
{
MakeFoo();
GC.Collect();
GC.Collect();
GC.Collect();
System.Console.ReadKey();
return 0;
}
private static void MakeFoo()
{
Leaker l = new Leaker();
}
}
internal class Leaker
{
private Timer t;
public Leaker()
{
t = new Timer(callback);
t.Change(1000, 0);
}
private void callback(object state)
{
System.Console.WriteLine("Still alive!");
t.Change(1000, 0);
}
}
Much like GlaDOS, the Leaker
object will be indefinitely "still alive" - yet, there is no way to access the object (except internally, and how can the object know when it's not referenced anymore?)
If you aren't referring to applications using .NET, which these answers discuss very well, but are actually referring to the runtime itself, then it technically can have memory leaks, but at this point the implementation of the garbage collector is probably nearly bug-free. I have heard of one case in which a bug was found where something in the runtime, or maybe just in the standard libraries, had a memory leak. But I don't remember what it was (something very obscure), and I don't think I would be able to find it again.
Well .NET has a garbage collector to clean things up when it sees fit. This is what separates it from other unmanaged languages.
But .NET can have memory leaks. GDI leaks are common among Windows Forms applications, for example. One of the applications I've helped develop experiences this on a regular basis. And when the employees in the office use multiple instances of it all day long it's not uncommon for them to hit the 10,000 GDI object limit inherent to Windows.
One major source of C/C++ memory leaks that effectively doesn't exist in .Net is when to deallocate shared memory
The following is from a Brad Abrams led class on Designing .NET Class Libraries
"Well, the first point is, of course, there are no memory leaks, right? No? There are still memory leaks? Well, there is a different kind of memory leak. How about that? So the kind of memory leak that we don’t have is, in the old world, you used to malloc some memory and then forget to do a free or add ref and forget to do a release, or whatever the pair is. And in the new world, the garbage collector ultimately owns all the memory, and the garbage collector will free that stuff when there are no longer any references. But there can still sort of be leaks, right? What are the sort of leaks? Well, if you keep a reference to that object alive, then the garbage collector can’t free that. So lots of times, what happens is you think you’ve gotten rid of that whole graph of objects, but there’s still one guy holding on to it with a reference, and then you’re stuck. The garbage collector can’t free that until you drop all your references to it.
The other one, I think, is a big issue. No memory ownership issue. If you go read the WIN32 API documentation, you’ll see, okay, I allocate this structure first and pass it in and then you populate it, and then I free it. Or do I tell you the size and you allocate it and then I free it later or you know, there are all these debates going on about who owns that memory and where it’s supposed to be freed. And many times, developers just give up on that and say, “Okay, whatever. Well, it’ll be free when the application shuts down,” and that’s not such a good plan.
In our world, the garbage collector owns all the managed memory, so there’s no memory ownership issue, whether you created it and pass it to the application, the application creates and you start using it. There’s no problem with any of that, because there’s no ambiguity. The garbage collector owns it all. "
Full Transcript
Remember, the difference between a cache and a memory leak is policy. If your cache has a bad policy (or worse, none) for removing objects, it is indistinguishable from a memory leak.
This reference shows how leaks can happen in .Net using weak event patterns. http://msdn.microsoft.com/en-us/library/aa970850.aspx
.NET can have memory leaks but it does a lot to help you avoid them. All reference type objects are allocated from a managed heap which tracks what objects are currently being used (value types are usually allocated on the stack). Whenever a new reference type object is created in .NET, it is allocated from this managed heap. The garbage collector is responsible for periodically running and freeing up any object that is no longer used (no longer being referenced by anything else in the application).
Jeffrey Richter's book CLR via C# has a good chapter on how memory is managed in .NET.
The best example I've found was actually from Java, but the same principle applies to C#.
We were reading in text files that consisted of many long lines (each line was a few MB in heap). From each file, we searched for a few key substrings and kept just the substrings. After processing a few hundred text files, we ran out of memory.
It turned out that string.substring(...) would keep a reference to the original long string... even though we kept only 1000 characters or so, those sub-strings would still use several MB of memory each. In effect, we kept the contents of every file in memory.
This is an example of a dangling reference that resulted in leaked memory. The substring method was trying to reuse objects, but ended up wasting memory.
Edit: Not sure if this specific problem plagues .NET. The idea was to illustrate an actual design/optimization performed in a garbage collected language that was, in most cases, smart and useful, but can result in a unwanted memory usage.
What about if you are using a managed dll but the dll contians unsafe code? I know this is spliting hairs, but if you dont have the source code, then from yourr point of view, you are only using managed code but you can still leak.
精彩评论