Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Marshal.GetFunctionPointerForDelegate leaks memory #353

Closed
jeffschwMSFT opened this issue Nov 27, 2019 · 10 comments · Fixed by #692
Closed

Marshal.GetFunctionPointerForDelegate leaks memory #353

jeffschwMSFT opened this issue Nov 27, 2019 · 10 comments · Fixed by #692

Comments

@jeffschwMSFT
Copy link
Member

jeffschwMSFT commented Nov 27, 2019

Copied from a VSFeedback item.

A customer noticed that repeated calls to a p/invoke that took a delegate appeared to be leaking memory. The underlying cause is the new way that we allocate umentrythunks and how GetFunctionPointerForDelegate caches them. Previously, the same thunk was returned and used when run in a tight loop (see code below). Now a new thunk is used every time, which results in memory seemingly increasing.

using System.Runtime.InteropServices;
 
namespace test
{
   class Program
   {
      static void Main(string[] args)
      {
         while (true)
         {
            var delg = new EnumWindowsProc(MyEnumProc);
            IntPtr ptr = Marshal.GetFunctionPointerForDelegate( delg ); 
            GC.Collect(); 
            GC.WaitForPendingFinalizers(); 
            GC.Collect(); 
         }
      }
      private static int MyEnumProc(IntPtr hwnd, IntPtr lParam)
      {
         return 0;
      }

      private delegate int EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
   }
}
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Nov 27, 2019
@jeffschwMSFT
Copy link
Member Author

Introduced by f9a161c

@jkotas
Copy link
Member

jkotas commented Nov 27, 2019

Now a new thunk is used every time, which results in memory seemingly increasing.

I see the thunks getting reused after a while. Yes, we use some extra fixed amount of memory to get better diagnostics after f9a161c, but it is not a memory leak.

I agree that we have a memory leak, but the problem are leaking GC handles. There is nothing that calls ~UMEntryThunk destructor. This bug was introduced in Windows Phone CoreCLR port and it was in .NET Core since day 1.

@jkotas jkotas changed the title Marshal.GetFunctionPointerForDelegate appears to leak memory based on how umentrythunks are now allocated Marshal.GetFunctionPointerForDelegate leaks memory Nov 27, 2019
@jkotas jkotas added the bug label Nov 27, 2019
@jkotas jkotas added this to the 5.0 milestone Nov 27, 2019
@mateoatr
Copy link
Contributor

mateoatr commented Dec 6, 2019

Yes, looks like the thunks are good. We create up to 64 and then reuse them, as @jkotas noted. However, everytime Marshal.GetFunctionPointerForDelegate gets called, we create a new weak long handle which seems a bit odd to me; since we're reusing the thunks, shouldn't we reuse the handles too?

My commit stops the leak, although I'm not sure if it is the correct approach.

@jkotas
Copy link
Member

jkotas commented Dec 7, 2019

@mateoatr Thanks for looking into this.

My commit stops the leak, although I'm not sure if it is the correct approach.

We want to destroy the GC handle for thunks that are not in use. Otherwise, the GC handles would be keeping user objects alive for no good reason that would be a different type of leak.

I think that the fix should be to move the code from ~UMEntryThunk to UMEntryThunk::Terminate, and delete ~UMEntryThunk.

@Gladskih
Copy link

What version of .net core has fix for this?

@jkotas
Copy link
Member

jkotas commented Feb 15, 2020

This fix will ship in .NET 5. It was not back-ported to older versions.

@avl
Copy link

avl commented Jun 20, 2020

Is this bug in .NET Framework 4.6.2? I'm about to deploy a long-running program that does a lot of GetFunctionPointerForDelegate, should I expect problems?

@jkotas
Copy link
Member

jkotas commented Jun 20, 2020

This bug was in .NET Core only.

@cnx-jd
Copy link

cnx-jd commented Aug 7, 2020

Is there a work around for this at all? Or any chance of getting this patched in .Net Core 3.X? We encountered this in a new project we're working on with .Net Core 3.1.
Having to wait for .Net 5.0 (or more likely .Net 6.0 for LTS) would probably mean we have to revert to .Net Framework for our release, something we'd prefer not to do.

@AaronRobinsonMSFT
Copy link
Member

@cnx-jd Thanks for asking about this again. Given the recurrent requests and the trivial change, we have decided to consider this for .NET Core 3.x servicing. The discussion for servicing and decision can be followed at dotnet/coreclr#28074.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants