Skip to content

Extremely high and unexpected memory commit sizes using DOTNET_GCHeapHardLimit #75538

@ANahr

Description

@ANahr

Description

We are running lots of instances of kestrel .net core processes on a server machines.
Using DOTNET_GCHeapHardLimitPercent we tried to limit the memory of a single process instance (to 128GB), so that it would be impossible for a single process to take all machine memory (256GB). This seems to work but has completely unexpected negative side effects on total memory consumption. Running the process requires about approx. 350-400MB of private memory and has a memory commit size as follows:

  • Reference Windows; 8 core; 32GB; NOT using DOTNET_GCHeapHardLimit: 0.6GB
  • Reference Windows; 8 core; 32GB; using DOTNET_GCHeapHardLimit: 2.0GB
  • Server Windows; 16 core; 256GB; using DOTNET_GCHeapHardLimit: 6.5GB
  • Server Windows; 28 core; 256GB; using DOTNET_GCHeapHardLimit: 10.5GB

We expect to run 100+ of those processes on one of the machines, but this is impossible with this commit sizes because the processes get an OutOfMemoryException (or other OOMs) instantly.
Worst case the commit size is 30 times larger than the private memory used!

Reproduction Steps

Please note that we are NOT running in containers.
Take a .net core process.
Set DOTNET_GCHeapHardLimitPercent to a high value (e.g. 75%) on a machine with lots of RAM (e.g. 256GB) many cores (e.g. 28) and observe an extremely high memory commit.

Expected behavior

Using DOTNET_GCHeapHardLimitPercent should not create extremely high memory commits compared to not using this switch. It should LIMIT memory, not enlarge it (and certainly not by unreasonable dimensions).

Actual behavior

If you set DOTNET_GCHeapHardLimit to large memory sizes it consumes high amounts of commit memory (much larger than when not setting DOTNET_GCHeapHardLimit at all) for no clear reason.
Please note that setting other switches seems to have no or little effect (see list below)

Here are some measurements with different settings on a machine with 8 cores, 32GB RAM:

[DEFAULT]
Physical: 380
Private: 340
Commit: 650

DOTNET_GCConserveMemory=9
Physical: 380
Private: 340
Commit: 650

DOTNET_GCHeapHardLimitPercent=A (10%)
Physical: 380
Private: 340
Commit: 620

DOTNET_GCHeapHardLimitPercent=5 (5%)
Physical: 350
Private: 310
Commit: 480

DOTNET_GCHeapHardLimitPercent=5A (90%)
Physical: 380
Private: 340
Commit: 2000

DOTNET_GCHeapHardLimitPercent=5A (90%)
DOTNET_GCHeapCount=4
Physical: 400
Private: 355
Commit: 2000

DOTNET_GCHeapHardLimitPercent=5A (90%)
DOTNET_GCHeapCount=1
Physical: 400
Private: 360
Commit: 2000

DOTNET_GCHeapHardLimitPercent=5A (90%)
DOTNET_GCHeapCount=1
DOTNET_GCHighMemPercent=5
Physical: 400
Private: 360
Commit: 2000

DOTNET_GCHeapHardLimitPercent=5A (90%)
DOTNET_GCHeapCount=1
DOTNET_GCConserveMemory=9
Physical: 300
Private: 350
Commit: 1900

DOTNET_GCHeapHardLimitPercent=5A (90%)
DOTNET_GCHeapCount=100
Physical: 380
Private: 340
Commit: 2000

Regression?

No response

Known Workarounds

None/not using DOTNET_GCHeapHardLimit at all

Configuration

.Net Core 6; Windows

Other information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions