SQLOS's memory manager: responding to memory pressure

Resource Monitor and Memory Pressure

When configuring SQL Server it is very important to understand how it reacts to memory pressure. I have already spent significant amount of time describing types of memory pressure. In this post you will understand why it is important. Memory pressure is categorized   into two major groups: VAS and physical. Physical memory pressure could be imposed by OS, we called external or it could be imposed by the process itself we call it internal.

 

SQLOS implements a complete framework to enable process's handling any type of memory pressure. In the heart of the framework lies Resource Monitor task, RM.  RM monitors state of the external and internal memory indicators. Once one of them changes, RM observes state of all indicators. Then it maps indicator's states into corresponding notification. Once notifications is calculated it broadcasts it to memory clerks.

                             

                            `         ------------------

                            | Resource Monitor|

                  / ------------------\

       /             |               \

 ------------------------------         -------------        ------------------------------  

| Low Physical Internal/External |       |  Low VAS   |       | High Physical Internal/External |

 ------------------------------         -------------         ------------------------------

 

Resource Monitor and Memory Clerks

 

Remember SQLOS has two types of nodes: memory node and cpu nodes. Memory nodes provide locality of allocations and cpu nodes provide locality of scheduling. Currently every cpu node has its own resource monitor. The reason is to be able to react to memory pressure on a given node - I will talk more about cpu nodes when covering SQLOS scheduling subsystem.  For now remember that depending on machine configuration there could be multiple RM tasks running at the same time.

 

Large memory consumers leverage memory clerks to allocate memory. One more important task of memory clerks is to process notifications from RM. A consumer can subscribe its clerk to receive memory pressure notifications and react to it accordingly. 

 

Every cpu node has a list of memory clerks. First RM calculates notification it needs to send. Then it goes through the list and broadcast notification to each memory clerk one by one. During the broadcast caches receive notification as well since they are memory clerks.

 

             

                                                      ------------------

                    ------------------------| Resource Monitor| -------------------------                         

                           /                                   ------------------                                     \ 

                          /                                             /         \                                            \

                         /                                            /            \                                             \

 ---------------------         -----------------------        --------------------------     ------------------

|Generic Memory Clerk|       |  Cache Memory Clerk  |        | Buffer Pool Memory Clerk |  | CLR Memory Clerk |

 ---------------------         -----------------------         --------------------------    ------------------

 

From RM's scheduling point of view there are couple of important points you need to be aware.

  1. Resource Monitor runs on its own scheduler, we called it hidden scheduler
  2. Resource Monitor runs in non-preemptive mode.
  3. DAC node doesn’t have its own Resource Monitor

 

 

There are several memory clerks that can respond to memory pressure. We already talked about caches. In addition every cpu node leverages its clerk to trim worker and system thread pools under memory pressure. Full text leverages its memory clerk to shrink shared memory buffers it shares with MSSearch. CLR uses its clerk to trigger GC. Buffer pool leverages its clerk to respond to external and VAS memory pressure only. (Why?)

 

External Memory pressure: RM and Buffer Pool

From SQLOS perspective Buffer Pool  is a single page allocator - extensively used memory manager.  External memory pressure is signaled by Windows. RM wakes up and broadcasts corresponding notification to clerks. Upon receiving the notification BP recalculates its target commit, amount of physical memory BP is allowed to consume.  Keep in mind that target commit can't be lower than configuration parameter specified through sp_configure min server memory and can't be higher of max server memory. If new target commit is lower than currently committed buffers, BP starts shrinking until external physical memory pressure disappears. During this process BP tries to decommit or in case of AWE free physical memory back to OS. Remember that in SQL2000 BP didn't react to physical memory pressure when running in AWE mode.

 

Internal Memory Pressure: BP and Resource Monitor

Shrinkage of BP causes internal memory pressure. This is one of the ways for BP to get process into internal physical memory pressure  What components BP notifies about internal memory pressure? Yes, you guessed correctly, SQLOS exposes a mechanism for BP to turn on RM's indicator corresponding to internal memory pressure. As you learned RM translates the indicator's signal to notification it will broadcast to clerks. BP has its clerk and will get RM's notification back. Oh no, we get into infinite loop!? Actually this is not the case because BP only monitors external physical memory pressure. It ignores internal physical memory pressure altogether.

 

There are couple other ways for internal physical pressure to appear. It could be caused by dynamically changing max server memory. In addition it could raise when 75% of BP's pages are stolen using SQLOS's single page allocator interface. By triggering internal physical memory pressure BP reclaims its pages from caches and other components currently consuming them.

 

VAS Memory Pressure

So far I discussed how SQLOS and consequently SQL Server handles physical external and internal memory pressure. Handling VAS pressure is harder because on Windows it is difficult to recognize it. There are two ways how RM gets notified about VAS pressure. The first way is for memory node to notify RM. When memory node Virtual or Shared memory interfaces fail to allocate a region of 4MB and below (RM doesn't get notified if size of a region above 4MB), memory node turns on RM's VAS low indicator. There also exists proactive way, when RM is running it probes VAS for 4MB size if such region no longer exists RM itself turns on VAS low signal and starts broadcasting corresponding notification.

 

Responding to VAS pressure is what makes Yukon different from SQL2000.  In SQL2000 for server is hard to recover once it gets into VAS pressure. In Yukon VAS pressure notification will be send to all memory clerks so they have opportunity to shrink. For example cpu node will shrink its threads, CLR might unload appdomains that currently not in use, network libs will shrink their network buffers.

 

You remember, when talking about SQLOS memory manager, I mentioned that in AWE mode BP is capable of reacting to VAS pressure? Here it all comes together. When BP receives VAS low notification it enumerates its 4MB VAS regions it reserved previously. If it finds 4MB region that is not currently in use or either used by database pages it can easily free it.

 

Monitoring memory pressure:

The subject won't be complete without taking a look at how one can monitor, diagnose, different types of pressures SQL Server gets exposed to. Yes, we made yours and our life simpler. There is dmv that you can take a look at to find out history of memory pressure.

 

Following query shows a set of last notification RM broadcasted:

 

select * from sys.dm_os_ring_buffers

where

ring_buffer_type='RING_BUFFER_RESOURCE_MONITOR'

 

(yes we have several different ring buffers that you can pick into :-), including schedulers, exceptions and OOMs, but these are subjects for different posts)

 

Here is the example  of query output.

 

<Record id = "0" type ="RING_BUFFER_RESOURCE_MONITOR" time ="788327260">

<ResourceMonitor>

<Notification>RESOURCE_MEMPHYSICAL_HIGH</Notification>

<Indicators>1</Indicators>

<NodeId>0</NodeId>

</ResourceMonitor>

<MemoryNode id="0">

<AvailableMemoryOnNode>0</AvailableMemoryOnNode>

<ReservedMemory>2111472</ReservedMemory>

<CommittedMemory>20944</CommittedMemory>

 

<SharedMemory>0</SharedMemory>

<AWEMemory>0</AWEMemory>

<SinglePagesMemory>1792</SinglePagesMemory>

<MultiplePagesMemory>6680</MultiplePagesMemory>

<CachedMemory>592</CachedMemory>

</MemoryNode>

<MemoryRecord>

<TotalPhysicalMemory>1047556</TotalPhysicalMemory>

<AvailablePhysicalMemory>542532</AvailablePhysicalMemory>

<TotalPageFile>3254476</TotalPageFile>

<AvailablePageFile>2242756</AvailablePageFile>

<TotalVirtualAddressSpace>2097024</TotalVirtualAddressSpace>

<AvailableVirtualAddressSpace>972352</AvailableVirtualAddressSpace>

<AvailableExtendedVirtualAddressSpace>0</AvailableExtendedVirtualAddressSpace>

</MemoryRecord>

</Record>

 

Following query shows when BP, single page allocator, turns on/off internal memory pressure

 

select * from sys.dm_os_ring_buffers

where

ring_buffer_type='RING_BUFFER_SINGLE_PAGE_ALLOCATOR'

 

<Record id = "9" type ="RING_BUFFER_SINGLE_PAGE_ALLOCATOR" time ="789165566">

<Pressure status="0"><AllocatedPages>477</AllocatedPages>

<AllAllocatedPages>477</AllAllocatedPages>

<TargetPages>31553</TargetPages>

<AjustedTargetPages>31553</AjustedTargetPages>

<CurrentTime>788967250</CurrentTime>

<DeltaTime>110</DeltaTime>

<CurrentAllocationRequests>79709</CurrentAllocationRequests>

<DeltaAllocationRequests>156</DeltaAllocationRequests>

<CurrentFreeRequests>79232</CurrentFreeRequests>

<DeltaFreeRequests>23640</DeltaFreeRequests>

 

</Pressure>

</Record>

 

Sorting the outputs from these two queries by time will allow you to observe the actual behavior of the SQL Server over time with respect to memory pressure.

 

If you are a careful reader most of the output from ring buffer queries should make sense to you by now. Some time latter on I will try to spend more time on detailed description of the output.

 

Conclusion:

 Memory pressure might significantly impact server performance and stability. Especially when SQL Server shares a box with other applications or shares its VAS with xps or CLR. Memory pressure might triger extra I/Os, recompilies, and other unnecessary activities. Understanding and diagnosing the types of memory pressure SQL Server is exposed to is very important part of managing your server and writing applications for it. I hope the information provided in this post will enable you to do your job more efficiently.

 

Have a good weekend!