diff options
author | Koundinya Veluri <kouvel@microsoft.com> | 2017-03-27 11:10:07 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-27 11:10:07 -0700 |
commit | 2401b6ed08252d48831bfd804c3533cd0142c76c (patch) | |
tree | 174456e9e6a8a0650abf3af80b722244b9b7449d /tests/src/baseservices | |
parent | 47d333c856e567884594e1efd3497a254b6a04fb (diff) | |
download | coreclr-2401b6ed08252d48831bfd804c3533cd0142c76c.tar.gz coreclr-2401b6ed08252d48831bfd804c3533cd0142c76c.tar.bz2 coreclr-2401b6ed08252d48831bfd804c3533cd0142c76c.zip |
Add heuristic to trigger GC to finalize dead threads and clean up han… (#10413)
Add heuristic to trigger GC to finalize dead threads and clean up handles and memory
A thread that is started allocates some handles in Thread::AllocHandles(). After it terminates, the managed thread object needs to be collected by a GC for the handles to be released. Some applications that are mostly idle but have a timer ticking periodically will cause a thread pool thread to be created on each tick, since the thread pool preserves threads for a maximum of 20 seconds currently. Over time the number of handles accumulates to a high value. Thread creation adds some memory pressure, but that does not force a GC until a sufficiently large handle count, and for some mostly idle apps, that can be very long. The same issue arises with directly starting threads as well.
Fixes #6602:
- Track a dead thread count separately from the current dead thread count. This is the count that will contribute to triggering a GC, once it reaches a threshold. The count is tracked separately so that it may be reset to zero when a max-generation GC occurs, preventing dead threads that survive a GC due to references from contributing to triggering a GC again in this fashion.
- If the count exceeds a threshold, enumerate dead threads to see which GC generation the corresponding managed thread objects are in. If the duration of time since the last GC of the desired generation also exceeds a threshold, trigger a preferably non-blocking GC on the finalizer thread.
- Removed a couple of handles and some code paths specific to user-requested thread suspension, which is not supported on CoreCLR
Diffstat (limited to 'tests/src/baseservices')
-rw-r--r-- | tests/src/baseservices/threading/DeadThreads/DeadThreads.cs | 43 | ||||
-rw-r--r-- | tests/src/baseservices/threading/DeadThreads/DeadThreads.csproj | 32 |
2 files changed, 75 insertions, 0 deletions
diff --git a/tests/src/baseservices/threading/DeadThreads/DeadThreads.cs b/tests/src/baseservices/threading/DeadThreads/DeadThreads.cs new file mode 100644 index 0000000000..cc9f054dea --- /dev/null +++ b/tests/src/baseservices/threading/DeadThreads/DeadThreads.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +public class DeadThreads +{ + /// <summary> + /// A sanity test that exercises code paths relevant to the heuristic that triggers GCs based on dead thread count and time + /// elapsed since a previous GC. See https://github.com/dotnet/coreclr/pull/10413. + /// + /// This test suite runs with the following environment variables relevant to this test (see .csproj): + /// set COMPlus_Thread_DeadThreadCountThresholdForGCTrigger=8 + /// set COMPlus_Thread_DeadThreadGCTriggerPeriodMilliseconds=3e8 // 1000 + /// </summary> + private static void GCTriggerSanityTest() + { + var testDuration = TimeSpan.FromSeconds(8); + var startTime = DateTime.UtcNow; + do + { + StartNoOpThread(); + Thread.Sleep(1); + } while (DateTime.UtcNow - startTime < testDuration); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void StartNoOpThread() + { + var t = new Thread(() => { }); + t.IsBackground = true; + t.Start(); + } + + public static int Main() + { + GCTriggerSanityTest(); + return 100; + } +} diff --git a/tests/src/baseservices/threading/DeadThreads/DeadThreads.csproj b/tests/src/baseservices/threading/DeadThreads/DeadThreads.csproj new file mode 100644 index 0000000000..b9c27581a9 --- /dev/null +++ b/tests/src/baseservices/threading/DeadThreads/DeadThreads.csproj @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <AssemblyName>DeadThreads</AssemblyName> + <ProjectGuid>{649A239B-AB4B-4E20-BDCA-3BA736E8B790}</ProjectGuid> + <OutputType>Exe</OutputType> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + </PropertyGroup> + <ItemGroup> + <Compile Include="DeadThreads.cs" /> + </ItemGroup> + <PropertyGroup> + <CLRTestBatchPreCommands><![CDATA[ +$(CLRTestBatchPreCommands) +set COMPlus_Thread_DeadThreadCountThresholdForGCTrigger=8 +set COMPlus_Thread_DeadThreadGCTriggerPeriodMilliseconds=3e8 +]]></CLRTestBatchPreCommands> + <BashCLRTestPreCommands><![CDATA[ +$(BashCLRTestPreCommands) +export COMPlus_Thread_DeadThreadCountThresholdForGCTrigger=8 +export COMPlus_Thread_DeadThreadGCTriggerPeriodMilliseconds=3e8 +]]></BashCLRTestPreCommands> + </PropertyGroup> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> +</Project> |