none
Memory Leak using EnumThreadWindows RRS feed

  • Question

  • Hey,

    I experience something weird when using EnumThreadWindows 

    I have this code:

        class Program
        {
            public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr lParam);
    
            [DllImport("user32", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool EnumThreadWindows(int threadId, EnumWindowProc callback, IntPtr lParam);
    
            static void Main(string[] args)
            {
                var a = true;
                while (a)
                {
                    foreach (var process in Process.GetProcesses())
                    {
                        foreach (ProcessThread thread in process.Threads)
                        {
                            var list = new List<IntPtr>();
                            EnumThreadWindows(thread.Id, (wnd, param) =>
                            {
                                list.Add(wnd);
                                return true;
                            }, IntPtr.Zero);
                            thread.Dispose();
                        }
                
                        process.Dispose();
                    }
    
                    GC.Collect();
                    Thread.Sleep(1);
                }
    
                GC.Collect();
                Console.WriteLine("End");
                Console.ReadLine();
            }
        }

    When I run this code I can clearly see that my unmanaged memory keep increasing without ever decreasing, even if I stop the while loop and wait, the program's unmanaged memory never decrease

    DotMemory screenshot: i.stack.imgur.com/0KlLW.png

    So I tried to change my code to use GCHandle.Alloc to pass the list like this:

    foreach (ProcessThread thread in process.Threads)
    {
        var list = new List<IntPtr>();
        var aloc = GCHandle.Alloc(list);
        var lParam = GCHandle.ToIntPtr(aloc);
        EnumThreadWindows(thread.Id, (wnd, param) =>
        {
            var list1 = GCHandle.FromIntPtr(param).Target as List<IntPtr>;
            list1?.Add(wnd);
            return true;
        }, lParam);
        thread.Dispose();
        aloc.Free();
    }
    process.Dispose();
    }

    When I use GCHandle.Alloc I don't have any memory issues anymore:

    i.stack.imgur.com/dga8N.png

    The thing is I want to understand why this is happening?

    My only idea is that maybe the code inside the EnumWindowProc is unmanaged and when I pass List<IntPtr> which is managed object to the unamanged code it becomes unmanaged and the GC can't clear it anymore.

    I may be lacking some understanding of something fundamental so I'd like any explanation :)

    Thanks!

    Edit: Sorry about the links for some reason I can't insert links because I need to verify my account, and I already did (Also can't find option to send the email again)
    • Edited by Lighto26 Saturday, July 13, 2019 6:15 PM
    Saturday, July 13, 2019 6:14 PM

All replies

  • When I run this code I can clearly see that my unmanaged memory keep increasing without ever decreasing, even if I stop the while loop and wait, the program's unmanaged memory never decrease

    It is a common beginner mistake to use external performance tools to watch memory use during execution of a program and think there is a memory leak because memory use grows during execution. Windows does not do clean-up unless it needs to. So memory that is freed by the application might remain allocated for Windows.

    Does VS say there is a memory leak? If not then there probably are not any.



    Sam Hobbs
    SimpleSamples.Info

    Sunday, July 14, 2019 4:46 PM
  • Hey, thanks for your answer.

    I didn't know that Windows does not clean-up memory unless it needs to, but the thing is that my actual program (The code I posted is only for explaining) runs noticeably slower because the memory is not freed. Now that I changed the code to use GCHandle.Alloc as explained above my program memory is stable and it does not slow down anymore.

    Also the screenshot I provided is indeed from the execution of the program but it stays the same even if I wait for hours, as long as I keep the while loop going the memory keep increasing and even if I stop it, it won't decrease.

    I also used Visual Studio to check the memory usage and I can seen same results (Which I can't post here for some reason, because my account is not verified), Also couldn't see any indicator for whether there is a memory leak, could you explain please?

    Thanks again

    Sunday, July 14, 2019 6:08 PM
  • Hi Light026,

    Thank you for posting here.

    Based on your description, you want to know why will occur memory leak when using EnumThreadWindows.

    I could not reproduce your problem even if I used Visual Studio to test it. I only find a little difference.

    Your initial code result:

    Changed code result:

    Besides, I want to know how do you use the Visual Studio to see unmanaged memory.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, July 15, 2019 5:34 AM
    Moderator
  • Hey,

    Thank you for answering!

    I just want to make sure we run everything in the same way - I created .NET Console app using Visual Studio 2019 Community and used the code above.

    If I run this code for something like an hour it can take 13GB of RAM. 

    Also I don't know how to analyze unmanaged memory using Visual Studio but I used DotMemory because I'm more familiar with it. (I used to diagnose memory leaks in the past but only managed, so this is new to me and I might be wrong)

    I uploaded the project to GitHub to make it easier to test:

    github.com/adirh3/MemoryLeakTest

    (Sorry about the link but I just can't verify my account for some reason)

    I also added screenshots of the Visual Studio memory analyzer for each case (5 mins each)

    Thanks again,

    Adir

    Monday, July 15, 2019 4:27 PM
  • Hi Ligh026,

    Thanks for the feedback.

    I have known the difference between the above two ways. In other way, I understand the usage of GCHandle.

    GCHandle will pin the list object,in this time, list is not controled by GC, it has been contonled by GCHandle. We need to use GCHandle.free method to release the object.

    This situation is mainly used to prevent memory leaks to use GCHandle when hosting and unmanaged code interact.

    Hope my explanation could be helpful.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, July 16, 2019 9:21 AM
    Moderator
  • Hey,

    Thanks again for the explanation! Actually this is exactly why I used GCHandle. 

    The problem is that I don't understand why the previous version even leaks in the first place... I realize that I don't understand something very basic here, and I want to understand what is it that I'm missing. 

    I asked because I want to learn, even though I have a (somewhat "hacky") solution.

    Thanks,

    Adir

    Tuesday, July 16, 2019 4:24 PM