C# Memory Leak Mystery
-
I'm trying to understand how the .NET framework manages memory and I've been using various profiling tools but there are still a few things that don't add up that maybe someone here can help me with. I've been using primarily devPartner studio with a sample project just to test a few things out. Let's start with the simplest of examples. If I create a loop that calls a dummy function that does nothing, something just to occupy memory, after the garbage collector runs a small amount of memory remains occupied. Granted it's only 324 bytes, but it should be 0 bytes and I need to figure out why. DevPartner Studio tells me I have 7 unreachable objects for a total of 208 bytes. My question is that if the garbage collector just ran, why are there any unreachable objects left? The rest of the memory (116 bytes) is occupied by an object array named "String Table" that I presume .NET was using to keep track of the strings I was allocating to consume memory. Seems the string array is no longer needed it would make sense if it were garbage collected, but if .NET wants to keep it around it really not a problem. At least that behavior is explainable. The 208 bytes of unreachable objects is a bigger problem. The problem becomes much more significant when I create an instance of a System.Data.SqlClient.SqlConnection and open it. After opening the connection, closing it, and garbage collection, I'm left with about 185K of memory that couldn't be cleaned up, about 150K of which is unreachable objects. I haven't yet tested the impact of opening multiple connections or repeatedly opening and closing the same connection. Any ideas will be greatly appreciated. Thanks, Jason
-
I'm trying to understand how the .NET framework manages memory and I've been using various profiling tools but there are still a few things that don't add up that maybe someone here can help me with. I've been using primarily devPartner studio with a sample project just to test a few things out. Let's start with the simplest of examples. If I create a loop that calls a dummy function that does nothing, something just to occupy memory, after the garbage collector runs a small amount of memory remains occupied. Granted it's only 324 bytes, but it should be 0 bytes and I need to figure out why. DevPartner Studio tells me I have 7 unreachable objects for a total of 208 bytes. My question is that if the garbage collector just ran, why are there any unreachable objects left? The rest of the memory (116 bytes) is occupied by an object array named "String Table" that I presume .NET was using to keep track of the strings I was allocating to consume memory. Seems the string array is no longer needed it would make sense if it were garbage collected, but if .NET wants to keep it around it really not a problem. At least that behavior is explainable. The 208 bytes of unreachable objects is a bigger problem. The problem becomes much more significant when I create an instance of a System.Data.SqlClient.SqlConnection and open it. After opening the connection, closing it, and garbage collection, I'm left with about 185K of memory that couldn't be cleaned up, about 150K of which is unreachable objects. I haven't yet tested the impact of opening multiple connections or repeatedly opening and closing the same connection. Any ideas will be greatly appreciated. Thanks, Jason
one of the possible reasons is objects that need explicit finalization (have defined finalizers). when they are not reachable anymore and GC runs, memory for these object is not reclaimed immediately. instead, objects are moved to the so called 'FReachable' queue and their finalizers ar run. the memory is reclaimed in the next GC round. you can find more details here[^] or here[^]
-
I'm trying to understand how the .NET framework manages memory and I've been using various profiling tools but there are still a few things that don't add up that maybe someone here can help me with. I've been using primarily devPartner studio with a sample project just to test a few things out. Let's start with the simplest of examples. If I create a loop that calls a dummy function that does nothing, something just to occupy memory, after the garbage collector runs a small amount of memory remains occupied. Granted it's only 324 bytes, but it should be 0 bytes and I need to figure out why. DevPartner Studio tells me I have 7 unreachable objects for a total of 208 bytes. My question is that if the garbage collector just ran, why are there any unreachable objects left? The rest of the memory (116 bytes) is occupied by an object array named "String Table" that I presume .NET was using to keep track of the strings I was allocating to consume memory. Seems the string array is no longer needed it would make sense if it were garbage collected, but if .NET wants to keep it around it really not a problem. At least that behavior is explainable. The 208 bytes of unreachable objects is a bigger problem. The problem becomes much more significant when I create an instance of a System.Data.SqlClient.SqlConnection and open it. After opening the connection, closing it, and garbage collection, I'm left with about 185K of memory that couldn't be cleaned up, about 150K of which is unreachable objects. I haven't yet tested the impact of opening multiple connections or repeatedly opening and closing the same connection. Any ideas will be greatly appreciated. Thanks, Jason
-
I'm trying to understand how the .NET framework manages memory and I've been using various profiling tools but there are still a few things that don't add up that maybe someone here can help me with. I've been using primarily devPartner studio with a sample project just to test a few things out. Let's start with the simplest of examples. If I create a loop that calls a dummy function that does nothing, something just to occupy memory, after the garbage collector runs a small amount of memory remains occupied. Granted it's only 324 bytes, but it should be 0 bytes and I need to figure out why. DevPartner Studio tells me I have 7 unreachable objects for a total of 208 bytes. My question is that if the garbage collector just ran, why are there any unreachable objects left? The rest of the memory (116 bytes) is occupied by an object array named "String Table" that I presume .NET was using to keep track of the strings I was allocating to consume memory. Seems the string array is no longer needed it would make sense if it were garbage collected, but if .NET wants to keep it around it really not a problem. At least that behavior is explainable. The 208 bytes of unreachable objects is a bigger problem. The problem becomes much more significant when I create an instance of a System.Data.SqlClient.SqlConnection and open it. After opening the connection, closing it, and garbage collection, I'm left with about 185K of memory that couldn't be cleaned up, about 150K of which is unreachable objects. I haven't yet tested the impact of opening multiple connections or repeatedly opening and closing the same connection. Any ideas will be greatly appreciated. Thanks, Jason
First off, how did you make the GC run?
GC.Collect
? That's not enough. You need to doGC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();Jason Pease wrote: If I create a loop that calls a dummy function that does nothing That's because the JIT compiler generates native code the first time a function is called and that code obviously takes up some space. If you'd noticed it carefully, you'd have seen that memory consumption doesn't keep rising, it just stays at that point. Nothing short of shutting down the process will reclaim that memory for you. Jason Pease wrote: The problem becomes much more significant when I create an instance of a System.Data.SqlClient.SqlConnection and open it. After opening the connection, closing it, IIRC, the underlying connection is not closed and the managed SqlConnection object is simply pooled, so obviously it can't be garbage collected. Jason Pease wrote: I'm left with about 185K of memory How did you measure? Task Manager? You should be using Perfmon with "#Bytes in All Heaps" counter to get an accurate picture of the size of the GC heap. Regards Senthil _____________________________ My Blog | My Articles | WinMacro
-
First off, how did you make the GC run?
GC.Collect
? That's not enough. You need to doGC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();Jason Pease wrote: If I create a loop that calls a dummy function that does nothing That's because the JIT compiler generates native code the first time a function is called and that code obviously takes up some space. If you'd noticed it carefully, you'd have seen that memory consumption doesn't keep rising, it just stays at that point. Nothing short of shutting down the process will reclaim that memory for you. Jason Pease wrote: The problem becomes much more significant when I create an instance of a System.Data.SqlClient.SqlConnection and open it. After opening the connection, closing it, IIRC, the underlying connection is not closed and the managed SqlConnection object is simply pooled, so obviously it can't be garbage collected. Jason Pease wrote: I'm left with about 185K of memory How did you measure? Task Manager? You should be using Perfmon with "#Bytes in All Heaps" counter to get an accurate picture of the size of the GC heap. Regards Senthil _____________________________ My Blog | My Articles | WinMacro
S. Senthil Kumar wrote: IIRC, the underlying connection is not closed and the managed SqlConnection Maybe it's because my profiler (devPartner Studio) doesn't allow me to see it, but this space only shows up as "Unreachable Objects"; it doesn't show me the type of object. I would have expected to see a pooled connection with some type of indicative name, not Unreachable Objects. This brings up the question though of where a pooled connection is kept. Seems I can utilize the same pooled connection from two different applications (right?), I would have expected it to not be included in the memory of my application space, but instead somewhere else. Maybe I need to study this more thoroughly. S. Senthil Kumar wrote: How did you measure? Task Manager? DevPartner tells me how much memory it is profiling. I've verified this number with the .NET counters and it seems to be reliable. Thanks for your help, Jason.
-
S. Senthil Kumar wrote: IIRC, the underlying connection is not closed and the managed SqlConnection Maybe it's because my profiler (devPartner Studio) doesn't allow me to see it, but this space only shows up as "Unreachable Objects"; it doesn't show me the type of object. I would have expected to see a pooled connection with some type of indicative name, not Unreachable Objects. This brings up the question though of where a pooled connection is kept. Seems I can utilize the same pooled connection from two different applications (right?), I would have expected it to not be included in the memory of my application space, but instead somewhere else. Maybe I need to study this more thoroughly. S. Senthil Kumar wrote: How did you measure? Task Manager? DevPartner tells me how much memory it is profiling. I've verified this number with the .NET counters and it seems to be reliable. Thanks for your help, Jason.
Jason Pease wrote: Seems I can utilize the same pooled connection from two different applications (right?) Really? I may be wrong, but I don't think that's possible. At best, they might be shared across all AppDomains in the same CLR instance. This[^] is a very good (and free) profiler that shows the actual objects in the GC heap. Regards Senthil _____________________________ My Blog | My Articles | WinMacro