Releasing Memory Pressure

Traditionally, in .NET applications we adjust the Garbage Collector to operate in server mode by modifying the App.config or Web.config files. However, Worker Roles work differently because your code is actually hosted by the WaWorkerHost.exe process. So you must modify the WaWorkerHost.exe.config file instead of app.config and web.config.

This article covers the Worker Role. However, the same concepts and techniques may be used to change the Web Roles mode by changing WaWebHost.exe.config instead of WaWorkerHost.exe.config.

WaWorkerHost.exe.config is in %approot%\base\x64

What is ServerGC & Why is it Important?

The Server Garbage Collection process allows us to reclaims objects that are no longer being used, clears memory, and makes it available for future allocations. In contrast to the Work Station Garbage Collector mode, the Server Garbage Collection mode aggressively releases memory and can prevent your roles from running out of memory due to memory bloat.

Reducing memory bloat allows us to increased density, which typically reduces overall operational costs in a pay for what you consume environment. Furthermore, Background Server Garbage Collection can increase performance.

Conditions for a Garbage Collection Include

  • The system has low physical memory.
  • The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.
  • The GC.Collect method is called. In almost all cases, you do not have to call this method, because the garbage collector runs continuously. This method is primarily used for unique situations and testing.

Enabling Background Server Garbage Collection

Leveraging Azure Role Starup Tasks, we call a CMD that executes a PowerShell script if the Azure Role is not executing in the Azure Emulator.

<!--
//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Microsoft Public License.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
-->
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="ServerModeGC.Solution" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2013-10.2.2">
  <WorkerRole name="GCServer.Worker" vmsize="Large">
    <Startup>
      <Task commandLine="ServerGC.cmd" executionContext="elevated" taskType="simple">
        <Environment>
          <Variable name="UseServerGC" value="True" />
          <Variable name="UseBackgroundGC" value="True" />
        </Environment>
      </Task>
    </Startup>
  </WorkerRole>
</ServiceDefinition>

The following Command Line pulls the desired configuration and applies them by executing the PowerShell script. Note that Azure Roles executing inside the Azure Emulator will not execute the PowerShell script.

REM *********************************************************
REM
REM     Copyright (c) Microsoft. All rights reserved.
REM     This code is licensed under the Microsoft Public License.
REM     THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
REM     ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
REM     IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
REM     PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
REM
REM *********************************************************

REM Check if the script is running in the Azure emulator and if so do not run
IF "%IsEmulated%"=="true" goto :EOF 

If "%UseServerGC%"=="False" GOTO :ValidateBackground
If "%UseServerGC%"=="0" GOTO :ValidateBackground
SET UseServerGC="True"

:ValidateBackground
If "%UseBackgroundGC%"=="False" GOTO :CommandExecution
If "%UseBackgroundGC%"=="0" GOTO :CommandExecution
SET UseBackgroundGC="True"

:CommandExecution

PowerShell.exe -executionpolicy unrestricted -command ".\GCSettingsManagement.ps1" -serverGC %UseServerGC% -backgroundGC %UseBackgroundGC%

Exit /b

This PowerShell script will create the required configuration file if it is missing or update the existing WaWorkerHost.exe.config file.

<#
//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Microsoft Public License.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#>
Param
(
	$serverGC = $True,
	$backgroundGC = $True
)

[string]$configFilePath = "$(${env:RoleRoot})\base\x64\WaWorkerHost.exe.config"

function Create-ConfigFileIfNotExists
{
	# Only create the Xml document if it does not already exist
	if(-not (Test-Path -Path $configFilePath -PathType Leaf))
	{
		[System.Xml.XmlDocument]$document = New-Object System.Xml.XmlDocument

		# config file doesn't exist create a now one
 		[System.Xml.XmlDeclaration]$prolog = $document.CreateXmlDeclaration("1.0", "utf-8", $null)
 		[System.Xml.XmlNode]$child = $document.AppendChild($prolog)
		[System.Xml.XmlElement]$configurationElement = Append-ElementIfNotExists $document $document.DocumentElement "configuration"

		# Save a copy of the document
		$document.Save($configFilePath)
	}
}

function Load-ConfigFile
{
	[System.Xml.XmlDocument]$document = New-Object System.Xml.XmlDocument

	#Check if the document already exists and load it if it does not
	if(Test-Path -Path $configFilePath -PathType Leaf)
	{
		$document.Load($configFilePath)
	}

	return $document
}

function Append-ElementIfNotExists
{
	param
	(
		[System.Xml.XmlDocument]$document,
		[System.Xml.XmlElement]$parent,
		[string]$elementName
	)
	[System.Xml.XmlElement]$element = $null
	[System.Xml.XmlNode]$parentNode = $parent

	if($document -ne $null)
	{
		if($parentNode -eq $null)
		{
			$parentNode = $document
		}

		$element = $parentNode.SelectSingleNode("./$($elementName)")

		if($element -eq $null)
		{
			$element = $document.CreateElement($elementName)
			[System.Xml.XmlElement]$child = $parentNode.AppendChild($element)
		}
	}

	return $element
}

function Create-ElementStructureIfNotExists
{
	param
	(
		[System.Xml.XmlDocument]$document
	)
	[bool]$isSuccess = $false

	if($document -ne $null)
	{
		[System.Xml.XmlElement]$configurationElement = Append-ElementIfNotExists $document $null "configuration"

		if($configurationElement -ne $null)
		{
			[System.Xml.XmlElement]$element = Append-ElementIfNotExists $document $configurationElement "runtime"

			$isSuccess = $element -ne $null
		}
	}

	return $isSuccess
}

# Create the document if required
Create-ConfigFileIfNotExists

# Load the configuration file into the XML document
[System.Xml.XmlDocument]$configurationDocument = Load-ConfigFile

if($configurationDocument -ne $null)
{
	if(Create-ElementStructureIfNotExists $configurationDocument)
	{
		# All of the entries are on the runtime element
		[System.Xml.XmlElement]$runtimeElement = $configurationDocument.DocumentElement.SelectSingleNode('./runtime')

		if($runtimeElement -ne $null)
		{
			# Set the Server GC to enabled if requested
			[System.Xml.XmlElement]$serverGCElement = Append-ElementIfNotExists $configurationDocument $runtimeElement "gcServer"
			$serverGCElement.SetAttribute("enabled", $serverGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower()) 

			# Set the concurrent GC to enabled if requested
			[System.Xml.XmlElement]$concurrentGCElement = Append-ElementIfNotExists $configurationDocument $runtimeElement "gcConcurrent"
			$concurrentGCElement.SetAttribute("enabled", $backgroundGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower())
		}
	}

	# Save the document
	$configurationDocument.Save($configFilePath)
}

Adding the Files to the Solution

Add the command and PowerShell files to the Worker Role that will be elevated. Place both files in the root directory of the Worker Role. Set the “Build Action” to “Content”, and set “Copy to Output Directory” to “Copy Always”.

Find Out More

4 responses to Releasing Memory Pressure on Memory Intensive #Azure Roles

  1. 

    Great post! But don’t forget to set IsEmulated variable when calling the .cmd in your startup task:

    Liked by 1 person

  2. 

    As far as I can tell (using .net 4.6.1 on Guest OS 5) this is no longer needed. My application is reporting running in Server GC+Interactive Latency.

    Like

  3. 

    As far as I can tell (using .net 4.6.1 on Guest OS 5) this is no longer needed. My application is reporting running in Server GC+Interactive Latency.

    Like

Trackbacks and Pingbacks:

  1. The Morning Brew - Chris Alcock » The Morning Brew #1698 - September 19, 2014

    […] Releasing Memory Pressure on Memory Intensive #Azure Roles – Alexandre Brisebois discusses the different modes for Garbage Collection in .NET and looks at how you can modify the mode in Web and Worker roles on the Azure Platform, sharing PowerShell scripts to help in the configuration. […]

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.