Skip to content

Commit a2f236c

Browse files
committed
Merge branch 'main' into rid-simplification
2 parents 9d86427 + 566ad4c commit a2f236c

File tree

87 files changed

+10038
-921
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+10038
-921
lines changed

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"[markdown]": {
3+
"editor.wordWrap": "off",
4+
"editor.wordWrapColumn": 120
5+
},
6+
}

INDEX.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ Use update-index to regenerate it:
3030
| 2018 | [Rolling self-contained .NET Core app deployments forward to the latest patch](accepted/2018/self-contained-roll-forward.md) | [Daniel Plaisted](https://github.com/dsplaisted) |
3131
| 2018 | [Standardized Environment Variables for CI Services](accepted/2018/build/standard-ci-env-variables.md) | [Tomáš Matoušek](https://github.com/tmat) |
3232
| 2018 | [Windows Compatibility Pack](accepted/2018/compat-pack/compat-pack.md) | [Immo Landwerth](https://github.com/terrajobst), [Wes Haggard](https://github.com/weshaggard) |
33-
| 2019 | [.NET Core GC Support for Docker Limits](accepted/2019/support-for-memory-limits.md) | [Rich Lander](https://github.com/richlander) |
33+
| 2019 | [.NET GC Support for Container Limits](accepted/2019/support-for-memory-limits.md) | [Rich Lander](https://github.com/richlander) |
3434
| 2019 | [Background](accepted/2019/targeting-packs-and-runtime-packs.md) | [Rich Lander](https://github.com/richlander), [Nick Guerrera](https://github.com/nguerrera) |
3535
| 2019 | [Runtime Binding Behavior](accepted/2019/runtime-binding.md) | [Rich Lander](https://github.com/richlander) |
3636
| 2019 | [System.Index and System.Range](accepted/2019/system-range/system-range.md) | [Immo Landwerth](https://github.com/terrajobst) |
3737
| 2020 | [.NET 5 Minimum OS Versioning](accepted/2020/minimum-os-version/minimum-os-version.md) | [Mikayla Hutchinson](https://github.com/mhutch) |
3838
| 2020 | [.NET Core 3.0 AppDomain Replacement Design and Guidance](accepted/2020/AssemblyLoadContext/AppDomainReplacement.md) | [Steve MacLean](https://github.com/sdmaclea) |
3939
| 2020 | [.NET Optional SDK Workloads](accepted/2020/workloads/workloads.md) | [Rich Lander](https://github.com/richlander) |
40-
| 2020 | [.NET Runtime Form Factors](accepted/2020/form-factors.md) | [Rich Lander](https://github.com/richlander), [Jan Kotas](https://github.com/jkotas) |
40+
| 2020 | [.NET Runtime Form Factors](accepted/2020/form-factors.md) | [Jan Kotas](https://github.com/jkotas), [Rich Lander](https://github.com/richlander) |
4141
| 2020 | [.NET SDK Workload Manifests](accepted/2020/workloads/workload-manifest.md) | [Mikayla Hutchinson](https://github.com/mhutch) |
4242
| 2020 | [Annotating APIs as unsupported on specific platforms](accepted/2020/platform-exclusion/platform-exclusion.md) | [Immo Landwerth](https://github.com/terrajobst) |
4343
| 2020 | [Annotating platform-specific APIs and detecting its use](accepted/2020/platform-checks/platform-checks.md) | [Immo Landwerth](https://github.com/terrajobst) |
@@ -76,23 +76,39 @@ Use update-index to regenerate it:
7676
| 2021 | [Make `System.Drawing.Common` only supported on Windows](accepted/2021/system-drawing-win-only/system-drawing-win-only.md) | [Santiago Fernandez Madero](https://github.com/safern) |
7777
| 2021 | [Objective-C interoperability](accepted/2021/objectivec-interop.md) | [Aaron Robinson](https://github.com/AaronRobinsonMSFT) |
7878
| 2021 | [Preview Features](accepted/2021/preview-features/preview-features.md) | [Immo Landwerth](https://github.com/terrajobst) |
79+
| 2021 | [Statics in Interfaces](accepted/2021/statics-in-interfaces/README.md) | [Tanner Gooding](https://github.com/tannergooding), [David Wrighton](https://github.com/davidwrighton), [Mads Torgersen](https://github.com/MadsTorgersen), [Immo Landwerth](https://github.com/terrajobst) |
7980
| 2021 | [Streamline Windows Forms application configuration and bootstrap](accepted/2021/winforms/streamline-application-bootstrap.md) | [Igor Velikorossov](https://github.com/RussKie) |
8081
| 2021 | [TFM for .NET nanoFramework](accepted/2021/nano-framework-tfm/nano-framework-tfm.md) | [Immo Landwerth](https://github.com/terrajobst), [Laurent Ellerbach](https://github.com/Ellerbach), [José Simões](https://github.com/josesimoes) |
8182
| 2021 | [Tracking Platform Dependencies](accepted/2021/platform-dependencies/platform-dependencies.md) | [Matt Thalman](https://github.com/mthalman) |
83+
| 2022 | [.NET 7 Version Selection Improvements](accepted/2022/version-selection.md) | [Rich Lander](https://github.com/richlander) |
84+
| 2023 | [.NET 8.0 Polyfill](accepted/2023/net8.0-polyfills/net8.0-polyfills.md) | [Immo Landwerth](https://github.com/terrajobst) |
85+
| 2023 | [Experimental APIs](accepted/2023/preview-apis/preview-apis.md) | [Immo Landwerth](https://github.com/terrjobst) |
86+
| 2023 | [Multi-threading on a browser](accepted/2023/wasm-browser-threads.md) | [Pavel Savara](https://github.com/pavelsavara) |
87+
| 2023 | [net8.0-browser TFM for applications running in the browser](accepted/2023/net8.0-browser-tfm.md) | [Javier Calvarro](https://github.com/javiercn) |
88+
| 2024 | [.NET Standard Targeting Recommendations](accepted/2024/net-standard-recommendation.md) | [Immo Landwerth](https://github.com/terrajobst), [Viktor Hofer](https://github.com/ViktorHofer) |
89+
| 2025 | [.NET SDK Acquisition and Management Tool](accepted/2025/cli-acquisition-tool.md) | [Chet Husk](https://github.com/baronfel), [Daniel Plaisted](https://github.com/dsplaisted) |
90+
| 2025 | [Annotating members as `unsafe`](accepted/2025/memory-safety/caller-unsafe.md) | [Andy Gocke](https://github.com/agocke) |
91+
| 2025 | [dotnet tool exec and dnx](accepted/2025/direct-tool-execution.md) | : [Marc Paine](https://github.com/marcpopMSFT) |
92+
| 2025 | [Memory Safety in .NET](accepted/2025/memory-safety/memory-safety.md) | [Richard Lander](https://github.com/richlander) |
93+
| 2025 | [Provide SDK hint paths in global.json](accepted/2025/local-sdk-global-json.md) | [Jared Parsons](https://github.com/jaredpar) |
94+
| 2025 | [RID-Specific .NET Tool Packages](accepted/2025/rid-specific-tool-packages.md) | [Daniel Plaisted](https://github.com/dsplaisted) |
8295

8396
## Drafts
8497

8598
|Year|Title|Owners|
8699
|----|-----|------|
87100
| 2021 | [Flexible HTTP APIs](accepted/2021/flexible-http.md) | [Cory Nelson](https://github.com/scalablecory), [Geoff Kizer](https://github.com/geoffkizer) |
88101
| 2021 | [Improve UTF-8 support](accepted/2021/utf8/README.md) | [Immo Landwerth](https://github.com/terrajobst) |
89-
| 2021 | [Statics in Interfaces](accepted/2021/statics-in-interfaces/README.md) | [Tanner Gooding](https://github.com/tannergooding), [David Wrighton](https://github.com/davidwrighton), [Mads Torgersen](https://github.com/MadsTorgersen), [Immo Landwerth](https://github.com/terrajobst) |
90102

91103
## Proposals
92104

93105
|Year|Title|Owners|
94106
|----|-----|------|
107+
| | [Add ability to embed install location options in apphost](proposed/apphost-embed-install-location.md) | |
95108
| | [Rate limits](proposed/rate-limit.md) | [John Luo](https://github.com/juntaoluo), [Sourabh Shirhatti](https://github.com/shirhatti) |
96109
| | [Readonly references in C# and IL verification.](proposed/verifiable-ref-readonly.md) | |
97110
| | [Ref returns in C# and IL verification.](proposed/verifiable-ref-returns.md) | |
111+
| | [SDK Analysis Level Property and Usage](proposed/sdk-analysis-level.md) | (PM) [Chet Husk](https://github.com/baronfel), (Engineering) [Daniel Plaisted](https://github.com/dsplaisted) |
112+
| | [Swift Interop](proposed/swift-interop.md) | [Andy Gocke](https://github.com/agocke), [Jeremy Koritzinsky](https://github.com/jkoritzinsky) |
113+
| | [Target AVX2 in R2R images](proposed/vector-instruction-set-default.md) | [Richard Lander](https://github.com/richlander) |
98114

Lines changed: 93 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,127 @@
1-
# .NET Core GC Support for Docker Limits
1+
# .NET GC Support for Container Limits
22

33
**Owner** [Rich Lander](https://github.com/richlander)
44

5-
.NET Core has support for [control groups](https://en.wikipedia.org/wiki/Cgroups) (cgroups), which is the basis of [Docker limits](https://docs.docker.com/config/containers/resource_constraints/). We found that the algorithm we use to honor cgroups works well for larger memory size limits (for example, >500MB), but that it is not possible to configure a .NET Core application to run indefinitely at lower memory levels. This document proposes an approach to support low memory size limits, <100MB.
5+
.NET has support for [control groups (cgroups)](https://en.wikipedia.org/wiki/Cgroups) , which is the basis of [Docker resource limits](https://docs.docker.com/config/containers/resource_constraints/). .NET has supported cgroups since .NET Core 2.1.
66

7-
Note: Windows has a concept similar to cgroups called [job objects](https://docs.microsoft.com/windows/desktop/ProcThread/job-objects). .NET Core should honor job objects in the same way as cgroups, as appropriate. This document will focus on cgroups throughout.
7+
Windows has a concept similar to cgroups called [job objects](https://docs.microsoft.com/windows/desktop/ProcThread/job-objects). .NET 6+ correctly honors job objects in the same way as cgroups. This document will focus on cgroups throughout.
88

9-
It is critical to provide effective and well defined experiences when .NET Core applications are run within memory-limited cgroups. An application should run indefinitely given a sensible configuration for that application. We considered relying on orchestators to manage failing applications (that can no longer satisfy the configuration of a cgroup), but believe this to be antithetical as a primary solution for building reliable systems. We also expect that there are scenarios where orchestrators will be unavailable or primitive or hardware will be constrainted, and therefore not tolerant of frequently failing applications. As a result, we need a better tuned algorithm for cgroup support to the end of running reliable software within constrained environments.
9+
It is critical to provide effective and well-defined capabilities for .NET applications within memory-limited cgroups. An application should run indefinitely given a sensible configuration for that application. It is important that .NET developers have good controls to optimize their container hosted applications. Our goal is that certain classes of .NET applications can be run with <100 MB memory constraints.
1010

11-
See [implementing hard limit for GC heap dotnet/coreclr #22180](https://github.com/dotnet/coreclr/pull/22180).
11+
Related:
12+
13+
- [implementing hard limit for GC heap dotnet/coreclr #22180](https://github.com/dotnet/coreclr/pull/22180).
14+
- [Validate container improvements with .NET 6](https://github.com/dotnet/runtime/issues/53149).
15+
- [Runtime configuration options for garbage collection](https://docs.microsoft.com/dotnet/core/runtime-config/garbage-collector)
16+
17+
## Cgroup constraints
18+
19+
cgroups control two main resources: memory and cores. Both are relevant to the .NET GC.
20+
21+
Memory constraints defines the maximum memory available to the cgroup. This memory is used by the guest operating system, the .NET runtime, the GC heap, and potentially other users. If a cgroup has `100 MB` available, the app will have less than that. The cgroup will be terminated (AKA `OOMKilled`) when the memory limit is reached.
22+
23+
Core constraints determine how many GC heaps should be created, at maximum. The maximum heap value matches `Environment.ProcessorCount`. There are three primary ways that this value can be set (using the `docker` CLI to demonstrate):
24+
25+
- Not specified -- `Environment.ProcessorCount` will match the total number of machine cores.
26+
- Via `--cpus` -- `Environment.ProcessorCount` uses that (decimal) value (rounded up to the next integer).
27+
- Via `--cpuset-cpus` -- `Environment.ProcessorCount` matches the count of specified CPUs.
28+
- Via `DOTNET_PROCESSOR_COUNT` -- `Environment.ProcessorCount` uses this value. If other values are also specified, they are ignored.
29+
30+
In the general case, there will be one heap per core. If the GC creates too many heaps, that can over-eagerly use up the memory limit, at least in part. There are controls to avoid that.
1231

1332
## GC Heap Hard Limit
1433

15-
The following configuration knobs will be exposed to enable developers to configure their applications:
34+
The *GC Heap Hard Limit* is the maximum managed heap size. It only applies when running within a cgroup. By default, it is lower than the cgroup memory constraint (AKA "the cgroup hard limit").
35+
36+
The following configuration knobs are exposed to configure applications:
1637

17-
* `GCHeapHardLimit` - specifies a hard limit for the GC heap as an absolute value
18-
* `GCHeapHardLimitPercent` - specifies a hard limit for the GC heap as a percentage of physical memory that the process is allowed to use
38+
* `GCHeapHardLimit` - specifies a hard limit for the GC heap as an absolute value, in bytes (hex value).
39+
* `GCHeapHardLimitPercent` - specifies a hard limit for the GC heap as a percentage of the cgroup hard limit (hex value).
1940

2041
If both are specified, `GCHeapHardLimit` is used.
2142

22-
The `GCHeapHardLimit` will be calculated using the following formular if it is not specified and the process is running inside a container (or cgroup or job object) with a memory limit specified:
43+
By default, the `GCHeapHardLimit` will be calculated using the following formula:
2344

2445
```console
25-
max (20mb, 75% of the memory limit on the container)
46+
max (20 MB, 75% of the memory limit on the container)
2647
```
2748

2849
The GC will more aggressive perform GCs as the GC heap grows closer to the `GCHeapHardLimit` with the goal of making more memory available so that the application can continue to safely function. The GC will avoid continuously performing full blocking GCs if they are not considered productive.
2950

3051
The GC will throw an `OutOfMemoryException` for allocations that would cause the committed heap size to exceed the `GCHeapHardLimit` memory size, even after a full compacting GC.
3152

32-
## GC Heap Heap Minimum Size
53+
## GC Heap Count
3354

3455
Using Server GC, there are multiple GC heaps created, up to one per core. This model doesn't scale well when a small memory limit is set on a machine with many cores.
3556

36-
The minimum _reserved_ segment size per heap: 16mb
57+
The heap count can be set two ways:
3758

38-
Example:
59+
- Manually via `DOTNET_GCHeapCount`.
60+
- Automatically by the GC, relying on:
61+
- Number of observed or configured cores.
62+
- A minimum _reserved_ memory size per heap of `16 MB`.
63+
64+
If [`DOTNET_PROCESSOR_COUNT`](https://github.com/dotnet/runtime/issues/48094) is set, including if it differs from `--cpus`, then the GC will use the ENV value for determining the maximum number of heaps to create.
65+
66+
Note: [.NET Framework 4.8 and 4.8.1](https://github.com/microsoft/dotnet-framework-docker/discussions/935) have the same behavior but `COMPlus_RUNNING_IN_CONTAINER` must be set. Also processor count is affected (in the same way) by `COMPlus_PROCESSOR_COUNT`.
67+
68+
Note: The next section talks about how many cores can be used by an application. It isn't defined in this doc, but is assumed to be per the [container runtime policy](https://docs.docker.com/config/containers/resource_constraints/#cpu).
69+
70+
Let's look at some examples. They are also demonstrated in [Testing GC Heap Counts with Containers](https://github.com/dotnet/runtime/issues/71413).
71+
72+
### Memory constrained; CPU unconstrained
73+
74+
```bash
75+
docker run --rm -m 256mb mcr.microsoft.com/dotnet/samples
76+
```
3977

4078
* 48 core machine
41-
* cgroup has a 200MB memory limit
79+
* cgroup has a 256 MB memory limit
4280
* cgroup has no CPU/core limit
43-
* 160MB `GCHeapHardLimit`
44-
* Server GC will create 10 GC heaps
81+
* 192 MB `GCHeapHardLimit`
82+
* Server GC will create 12 GC heaps, with 16 MB reserved memory
4583
* All 48 cores can be used by the application
4684

47-
Example:
85+
```
86+
heaps = (256 * .75) / 16
87+
heaps = 12
88+
```
89+
90+
### Memory and CPU constrained
91+
92+
```bash
93+
docker run --rm -m 256mb --cpus 2 mcr.microsoft.com/dotnet/samples
94+
```
4895

4996
* 48 core machine
50-
* cgroup has a 200MB memory limit
51-
* cgroup has 4 CPU/core limit
52-
* 160MB `GCHeapHardLimit`
53-
* Server GC will create 4 GC heaps
54-
* Only 4 cores can be used by the application
97+
* cgroup has a 256 MB memory limit
98+
* cgroup has 2 CPU/core limit
99+
* 192 MB `GCHeapHardLimit`
100+
* Server GC will create 2 GC heaps
101+
* 2 cores can be used by the application
55102

56-
## Previous behavior
103+
### Memory and CPU constrained (with CPU affinity):
104+
105+
```bash
106+
docker run --rm -m 256mb --cpuset-cpus 0,2,3 mcr.microsoft.com/dotnet/samples
107+
```
57108

58-
Previously, the **maximum GC heap size** matched the cgroup limit.
109+
* 48 core machine
110+
* cgroup has a 256 MB memory limit
111+
* cgroup has 3 CPU/core limit
112+
* 192 MB `GCHeapHardLimit`
113+
* Server GC will create 3 GC heaps
114+
* 3 cores can be used by the application
115+
116+
### Memory and CPU constrained (overriden by `DOTNET_PROCESSOR_COUNT`):
117+
118+
```bash
119+
docker run --rm -m 256mb --cpus 2 -e DOTNET_PROCESSOR_COUNT=4 mcr.microsoft.com/dotnet/samples
120+
```
121+
122+
* 48 core machine
123+
* cgroup has a 256 MB memory limit
124+
* cgroup has 2 CPU/core limit
125+
* 192 MB `GCHeapHardLimit`
126+
* Server GC will create 4 GC heaps
127+
* 2 cores can be used by the application

0 commit comments

Comments
 (0)