-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
HANDLEs is only freed after GC when try to sync wait an Task on windows? #47752
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
|
Yes. After a brief comparison between source.dot.net and referencesource.microsoft.com :
I don't see waithandle creation in both of them. |
runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs Lines 2876 to 2878 in 4df29c9
This should be the really related thing. It says that no handle should be created at all. |
Tagging subscribers to this area: @tarekgh Issue DetailsDescriptionExecute the below program in net5.0/netcoreapp3.1/2.1. Open taskmgr,procexp, processhack( It's not an leak, since the HANDLEs will still be freed after GC, but may long time latter if no other alloc happens. using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Test0
{
class Program
{
static TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
static void Main(string[] args)
{
Task.Run(async () =>
{
while (true)
{
Interlocked.Exchange(ref tcs, new TaskCompletionSource<int>()).TrySetResult(0);
await Task.Delay(10);
}
});
int i = 0;
while (true)
{
//Task.Delay(10).Wait();
tcs.Task.Wait();
//++i;
//if ((i % 1000) == 0)
// GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
}
return;
}
}
} Configuration
Regression?Maybe, since it not happen with net48. Other informationI know sync block an task looks strange, but it's used by some old library to simulate Thread.Sleep in netstandard1.x before the api is avaliable.
|
I don't see the cited behavior. I copy/pasted your code into a 64-bit .NET 5 app, ran it on Windows 10, and it's holding steady at ~180 handles per Task Manager. |
Seems it always need an GC to free handles, but on net48, the max count seems much more smaller.(up to 1 to 3k in net48, and 30 to 50k in net5.0 on the same PC, and then it will be collected and increase from 1xx again) @stephentoub using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime;
using System.Threading;
using System.Threading.Tasks;
namespace TestAnyThing
{
class Program
{
static volatile object sObj = new object();
static void Main()
{
using (var proc = Process.GetCurrentProcess())
{
Console.WriteLine(proc.ProcessorAffinity.ToString("X8"));
Console.WriteLine(GCSettings.IsServerGC);//False
Console.WriteLine(GCSettings.LatencyMode);//Interactive
if (Environment.ProcessorCount >= 2)
proc.ProcessorAffinity = (IntPtr)0x03;
}
new Thread(() =>
{
while (true)
{
var oldObj = sObj;
if (oldObj != null)
{
lock (oldObj)
{
Monitor.PulseAll(oldObj);
}
oldObj = null;
}
Thread.Sleep(10);
}
}).Start();
int i = 0;
var sw = Stopwatch.StartNew();
while (true)
{
//Task.Delay(10).Wait();
var obj = new object();
sObj = obj;
lock(obj)
{
Monitor.Wait(obj);
}
sObj = obj = null;
++i;
if ((i & 0x3F) == 0)
{
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
using (var proc = Process.GetCurrentProcess())
{
//net35: 4xx??, net48:1k~3k, net5.0:30~50k
Console.WriteLine($"{sw.ElapsedMilliseconds} ms used, {proc.HandleCount} handlers active.");
sw.Reset();
sw.Start();
}
}
}
}
}
} |
Description
Execute the below program in net5.0/netcoreapp3.1/2.1.
Open taskmgr,procexp, processhack(
show unamed handlers=true
) or any other tools to monitor the HANDLEs number used by the program. And see it(type=event) keeps grow about 20/s.It's not an leak, since the HANDLEs will still be freed after GC, but may long time latter if no other alloc happens.
Configuration
Regression?
Maybe, since it not happen with net48.No, it always need an GC to free handles, but on net48, the max count seems smaller.(1 to 3k in net48, and 30 to 50k in net5.0 on the same PC)
Other information
I know sync block an task looks strange, but it's used by some old library to simulate Thread.Sleep in netstandard1.x before the api is avaliable.
https://github.com/Azure/DotNetty/blob/dev/src/DotNetty.Common/Concurrency/XThread.cs#L93
The text was updated successfully, but these errors were encountered: