Enable Server GC mode for your worker role

If you are using an Azure worker role that is memory intensive you may notice that the performance of your application is not optimal.
One of the symptoms is a high CPU utilization combined with a high value for the "% Time in GC" performance counter. Typical workloads are WCF applications that handle many client requests.

You might be able to improve the performance quite a bit by using Server GC mode instead of Workstation GC mode

Note: there are many other possible reasons for performance problems apart from the GC mode. I suggest looking at Fundamentals of Garbage Collection and  Garbage Collection and Performance for more details .

When using the Workstation GC mode the garbage collection occurs on the thread that trigged the GC.Server GC creates a separate managed heap per processor and also a dedicated GC thread for each heap. This enables a balancing of allocation request across these heaps and the garbage collection can be performed in parallel on each of the managed heaps.

The default mode for garbage collection is the workstation mode but you can use the <gcServer> element in your application configuration file to specify the type of garbage collection. 
Note that IIS is automatically using the ServerGC mode  thus if you host your WCF application in a webrole you already have Server GC
However for a worker role the default is to use the workstation garbage collection. 

Starting with .NET 4.5 a new garbage collection mode called Background Server GC is available which eliminates the need to suspend threads during a generation 2 collection. Please have a look at Fundamentals of Garbage Collection for all the details.

To benefit from this mode the .NET runtime 4.5 has to be installed (Default on Server 2012).  Since .NET 4.5 is an in-place update for .NET 4.0 you can use Background Server GC even you applications are targeting .NET 4.0.

 

How to enable Server GC for your worker role

If you read the MSDN documentation for the <GCServer> Element you might think that you only have add the gcServer element to your app.config file. However this will not work because the GC mode needs to be set when the CLR runtime is initialized and your app.config file is transformed in <workerrole>.dll.config and loaded
after the runtime has been started. Therefore the setting must be applied to waworkerhost.exe.config file in %ROLEROOT%\base\x64 . Currently the Azure SDK tools are not providing a mechanism to directly apply the setting to waworkerhost.exe.config

To change the settings you can use a startup task to perform the required changes.

This is a sample PowerShell script that applies the required changes

 

  1. Define the startup task in the ServiceDefinition

    <WorkerRole name="WorkerRole1" vmsize="Medium">
        <Startup>
          <Task commandLine="myStartup.cmd" executionContext="elevated" taskType="simple" />
        </Startup>
    ...

  2. Bootstrap the PowerShell script

    REM myStartup.cmd
    @echo off
    powershell -command "Set-ExecutionPolicy Unrestricted"
    powershell
    .\setServerGC.ps1 2>> err.out

     

  3. And finally do the real work in PowerShell

 

 #Configure Server GC and Background GC settings 
Function ChangeGCSettings
{
 param ([string]$filename,[bool]$enableServerGC,[bool]$enableBackgroundGC)
 
 $xml = New-Object XML
 if (Test-Path ($filename))
 {
 $xml.Load($filename)
 }
 else
 {
 #config file doesn't exist create a now one
 [System.Xml.XmlDeclaration]$xmlDeclaration = $xml.CreateXmlDeclaration("1.0","utf-8",$null)
 $xml.AppendChild($xmlDeclaration)
 }
 
 # Check if we already have a configuration section - if not create it
 $conf = $xml.configuration 
 if ($conf -eq $null)
 {
 $conf = $xml.CreateElement("configuration")
 $xml.AppendChild($conf)
 }
 
 # Check if we already have a runtime section - if not create it
 $runtime = $conf.runtime
 if ($runtime -eq $null)
 {
 #runtime element doesn't exist 
 $runtime = $xml.CreateElement("runtime")
 $conf.AppendChild($runtime)
 }
 
 # configure ServerGC
 $gcserver = $runtime.gcServer
 if ($gcserver -eq $null)
 {
 $gcserver = $xml.CreateElement("gcServer")
 $gcserver.SetAttribute("enabled",$enableServerGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower())
 $runtime.AppendChild($gcserver);
 }
 else
 {
 $gcserver.enabled=$enableServerGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower()
 }
 
 # configure Background GC
 # .NET 4.5 is required for Bacground Server GC 
 # since 4.5 is an in-place upgrade for .NET 4.0 the new Background Server GC mode is available 
 # even if you target 4.0 in your application as long as the .NET 4.5 runtime is installed (Windows Server 2012 or higher by default)
 # 
 # See https://blogs.msdn.com/b/dotnet/archive/2012/07/20/the-net-framework-4-5-includes-new-garbage-collector-enhancements-for-client-and-server-apps.aspx
 
 $gcConcurrent = $runtime.gcConcurrent 
 if ($gcConcurrent -eq $null)
 {
 $gcConcurrent = $xml.CreateElement("gcConcurrent")
 $gcConcurrent.SetAttribute("enabled",$enableBackgroundGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower())
 $runtime.AppendChild($gcConcurrent);
 }
 else
 {
 $gcConcurrent.enabled=$enableBackgroundGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower()
 }
 $xml.Save($filename)
}
 
# Enable Background Server GC
ChangeGCSettings "${env:RoleRoot}\base\x64\WaWorkerHost.exe.config" $true $true