-1

After some reading I found out that everything you code in C# is managed code, and shouldn't give memory leaks. Yet my program behaves in a manner which suggests some sort of memory leak.

My entire program comes down to this: (currently I don't possess the actual code)

while(true)
{
    //Source of the "leak"
    List<object> _objects = ReturnAllWindows(); 

    //Do something awesome with the list...

    System.Threading.Thread.Sleep(10);
}

ReturnAllWindows is a method that uses pinvoke in combination with user32.dll and EnumWindows to get all the windows that are currently open.

When I run my program, the memory immediately skyrockets until I get an OutOfMemoryException.

From what I read, the only thing I can think of is that there is some sort of memory leak in the EnumWindows function, but I can hardly imagine that user32 isn't fully managed.

So what is going on? And how can I prevent / work around it?


EDIT: Solved it, the problem was that this list is later used in combination with some multithreading which wasn't handled properly. If you comment the ReturnAllWindows line, the multithreading was never reached, the platform invoke wasn't the problem at all.
Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
Jordy
  • 1,816
  • 16
  • 29
  • Have you tried calling `GC.Collect()`? It does seem odd, that you'd get an `OutOfMemoryException`, when calling `GC.Collect()` would've avoided the exception, but that's just how things are... – Nolonar May 22 '13 at 11:41
  • "everything you code in C# is managed code" : Not exactly. You can use the [unsafe](http://msdn.microsoft.com/en-us/library/t2yzs44b.aspx) keyword to access pointers. – cvraman May 22 '13 at 11:42
  • Do you have the code to GetWindows()? How is it allocating memory? If it's using global/thread memory to create the list or creating a list and storing it in a global/thread variable, it could cause your list to grow out of control. Without the code to GetWindows(), it's going to be hard to diagnose. – Richard May 22 '13 at 11:44
  • pinvoke will make you invoke unmanaged code, and will of course need to be disposed properly. And managed code is not fre from memory leaks, it is really simple to create a leak if you aren't careful, but you don't need to free up resources manually for each allocation. – jishi May 22 '13 at 11:49
  • @Nolonar I indeed tried GC.Collect(), the problem stayed. – Jordy May 22 '13 at 11:49
  • Show the code for GetWindows(), but my guess is that you would need to have a ReleaseWindows() method that would take your list of objects and free up the resources. You need to consider the pinvoke-code as unmanaged. – jishi May 22 '13 at 11:51
  • @Richard and Jishi I posted some extra code. – Jordy May 22 '13 at 11:58
  • There's no leak in anything that you posted. Why don't you post an entire program? – David Heffernan May 22 '13 at 12:33
  • The code is very sloppy, high odds for bad pinvoke happening here and imbalancing the stack. EnumWindows is declared wrong, the Callback declaration looks very suspicious without seeing how Report ends up calling GetText. It will also crash in the Release build when the delegate gets garbage collected. Imbalancing the stack is enough to get this to crash with the callback firing constantly enumerating the same window. Use pinvoke.net to find correct pinvoke declarations. – Hans Passant May 22 '13 at 13:06
  • @HansPassant, the code is based on what I found at pinvoke.net. And there is a good chance I left out a minor method in the code posted. But the problem wasn't in the pinvoke section after all. – Jordy May 22 '13 at 13:09
  • If the pinvoke was in the ReturnAllWindows, and you comment that out, then you never run pinvoke, am I right? – jishi May 22 '13 at 13:21

1 Answers1

1

It is very likely that if it is platform invoking (that is, calling a native, non-managed method from managed code) that memory is not being completely managed one way or another. Think about this for a moment. A native, non managed application is never written with the intent to support being called from managed code. So objects exist as unmanaged memory that is controlled by some lower level system (if one was developed) or not controlled. So calling a native method from managed code does what's called boxing, which creates a managed object that wraps the native memory.

So are there memory leaks in .NET? Technically, no. Not in the traditional sense. But the idea that garbage exists is definitely true. And dealing with boxing only further confuses (for lack of a better term) the GC.

I would think that somewhere in the awesome manipulation of your list of boxed data that there should be a dereference of the object. For example, if you are iterating through the collection of data using a foreach, and after each cycle completes you no longer need the data, you should remove all managed references to it. For example, consider the following example:

List<object> foo = new List<object>(); // Imagine this is your list of data

foreach (var item in foo)
{
    // Execute an awesome expression here
}

Seemingly innocuous, these expressions never release the memory because it's always referenced in one way or another. A memory optimized version of this would be as follows:

List<object> foo = new List<object>(); // Imagine this is your list of data

while (foo.Count > 0)
{
    var item = foo[0];

     // Execute an awesome expression here

     item = null;
     foo.RemoveAt(0);
}

Notice how in the second example there are no remaining references to the resources, and the boxed data is now eligible for collection. Now this may not be the complete problem, as memory issues in .NET are often very difficult to trace. Perhaps consider profiling your application to better understand your particular problems. Hope this helps.

Will Custode
  • 4,576
  • 3
  • 26
  • 51