SQL Server Memory Grants Pending above zero: queries queued before they can run

Memory Grants Pending is a SQLServer:Memory Manager counter that should almost always be zero. When it rises above zero, queries are fully parsed and optimized but cannot start execution because the query workspace memory pool is exhausted. Applications see queries that appear hung: connections stay open, latency climbs, and CPU and I/O may look idle.

The counter pairs with the RESOURCE_SEMAPHORE wait type. The counter shows how many queries are queued right now. The wait type aggregates how long they spent in that queue. Both point at the same bottleneck: SQL Server cannot honor a memory grant request because the workspace memory pool has been drained by other queries, by one oversized grant from a bad plan, or by external OS pressure on max server memory.

A value of 1 or 2 during a known heavy reporting window may be tolerable. Sustained at 1 or higher under normal OLTP load warrants investigation. Five or more pending grants during normal OLTP operations means queries are timing out, spilling, or both, and user impact is already happening.

What this means

A memory grant is workspace memory a query reserves before execution begins. Queries with sort, hash, and certain join operators need that workspace to stage rows. The grant is sized by the optimizer using cardinality estimates. If the optimizer thinks a query will produce 10,000 rows, it requests enough memory to sort 10,000 rows. If the estimate is wrong by 10x, the grant is wrong by roughly 10x.

The grant comes from a pool bounded by max server memory, separate from the data-page buffer pool. When concurrent memory-intensive queries drain the pool, new requests queue. They show up in sys.dm_exec_query_memory_grants with grant_time = NULL and granted_memory_kb = NULL. While queued, they record RESOURCE_SEMAPHORE waits. If a query waits long enough, SQL Server returns error 8645 and kills the request. If the wait resolves with a smaller-than-requested grant, the query runs but spills sort or hash intermediates to TempDB. The memory wait goes away, but a new I/O bottleneck appears in TempDB.

flowchart TD
    Q[Query with sort or hash] --> REQ[Grant request from workspace pool]
    REQ --> POOL{Pool has free memory?}
    POOL -->|Yes| RUN[Grant honored, runs in memory]
    POOL -->|No| QUEUE[Queue on RESOURCE_SEMAPHORE]
    QUEUE --> CNT[Memory Grants Pending + 1]
    QUEUE --> OUTCOME{Resolves how?}
    OUTCOME -->|Timeout| ERR[Error 8645, query killed]
    OUTCOME -->|Smaller grant| SPILL[Sort/hash spills to TempDB]
    OUTCOME -->|Full grant| RUN

A counter reading of zero does not mean the system is healthy. It only means no query is currently queued. Queries that were granted less than they asked for are still running, still slow, and still spilling.

Common causes

CauseWhat it looks likeFirst thing to check
Bad plan with oversized grantOne query has requested_memory_kb 10x larger than similar queries, RESOURCE_SEMAPHORE is the dominant wait, other queries spillsys.dm_exec_query_memory_grants ordered by requested_memory_kb DESC
Too many concurrent memory-intensive queriesMany queries each hold a grant, batch requests/sec is climbing, PLE is droppingCount of rows in sys.dm_exec_query_memory_grants and the workload mix
Max server memory too lowOS shows available memory but SQL Server is capped, PLE is low, free memory clerks shrinksp_configure 'max server memory', sys.dm_os_process_memory
Resource Governor cap too restrictiveOnly one workload group queues, RESOURCE_SEMAPHORE waits concentrate in one pool, Standard Edition unaffectedsys.dm_resource_governor_workload_groups (Enterprise only)
External OS memory pressureprocess_physical_memory_low = 1, OS reports low memory, other processes competingsys.dm_os_sys_memory.system_memory_state_desc

Quick checks

Run these read-only queries against the instance. None of them modify state.

-- Current Memory Grants Pending counter value
SELECT cntr_value AS memory_grants_pending
FROM sys.dm_os_performance_counters
WHERE counter_name = 'Memory Grants Pending'
  AND object_name LIKE '%Memory Manager%';

-- Detailed view of waiting and granted queries
SELECT session_id, request_time, grant_time,
       requested_memory_kb, granted_memory_kb,
       required_memory_kb, used_memory_kb,
       wait_time_ms, dop, query_cost
FROM sys.dm_exec_query_memory_grants
ORDER BY wait_time_ms DESC;

-- RESOURCE_SEMAPHORE wait totals
SELECT waiting_tasks_count, wait_time_ms,
       max_wait_time_ms, signal_wait_time_ms
FROM sys.dm_os_wait_stats
WHERE wait_type = 'RESOURCE_SEMAPHORE';

-- OS-level memory state
SELECT total_physical_memory_kb / 1024 AS total_mb,
       available_physical_memory_kb / 1024 AS available_mb,
       system_memory_state_desc
FROM sys.dm_os_sys_memory;

-- SQL Server process memory state
SELECT physical_memory_in_use_kb / 1024 AS sql_physical_mb,
       process_physical_memory_low,
       process_virtual_memory_low
FROM sys.dm_os_process_memory;

-- Top memory clerks (where SQL Server memory lives)
SELECT TOP 10 type, name, pages_kb / 1024 AS size_mb
FROM sys.dm_os_memory_clerks
WHERE pages_kb > 0
ORDER BY pages_kb DESC;

Avoid ordering or aggregating sys.dm_exec_query_memory_grants with expensive operators when the instance is under pressure. The DMV itself consumes memory to satisfy the query.

How to diagnose it

  1. Confirm the counter is sustained nonzero. Memory Grants Pending is a point-in-time counter. Sample every few seconds and look for sustained values, not a single blip during a backup window.

  2. Open sys.dm_exec_query_memory_grants. Rows with grant_time = NULL and granted_memory_kb = NULL are waiting. wait_time_ms shows how long each has been queued. Sort by wait_time_ms DESC to find the oldest waiters.

  3. Sort by requested_memory_kb DESC. A single outlier requesting 50GB when peers request 5GB is almost always a cardinality estimation problem or a parameter sniffing regression. Pull the query text with sys.dm_exec_sql_text and the plan with sys.dm_exec_query_plan.

  4. Snapshot RESOURCE_SEMAPHORE in sys.dm_os_wait_stats twice, 60 seconds apart, and compute the delta. Rising waiting_tasks_count and wait_time_ms during the window confirms active pressure.

  5. Verify the OS picture. If system_memory_state_desc reports “Available physical memory is low”, the SQL Server process is being squeezed at the OS level. Look for co-located processes, VM balloon drivers, or max server memory set too high (leaving too little for the OS).

  6. Map TempDB spills. If Memory Grants Pending is zero or low but TempDB internal_object_reserved_page_count (from sys.dm_db_file_space_usage) is climbing and sort or hash warnings appear, queries are running with undersized grants and spilling. The counter and the spills are two halves of the same problem.

  7. For sustained pressure, pull the query text and the execution plan for the largest grant requesters. Look for sort or hash operators with estimated row counts that diverge from actual row counts. If Query Store is enabled, the runtime statistics in sys.query_store_runtime_stats show actual rows per plan node. Otherwise, re-run the query with actual execution plan enabled in SSMS or Azure Data Studio. Check statistics freshness on the underlying tables.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Memory Grants Pending (Memory Manager)Direct count of queued queriesSustained nonzero under OLTP
RESOURCE_SEMAPHORE wait timeAggregate wall time spent waitingDelta rising over 60s samples
sys.dm_exec_query_memory_grants rows with grant_time NULLLive queue depthAny sustained rows
granted_memory_kb less than requested_memory_kb (both non-NULL)Query received a reduced grantSort or hash spills to TempDB
TempDB internal object pages (internal_object_reserved_page_count)Spill landing zoneClimbing during normal OLTP
Page Life Expectancy (Buffer Manager)Buffer pool pressure correlateSudden 50%+ drop from baseline
system_memory_state_descOS-level pressure“Available physical memory is low”
Sort Warnings, Hash Warnings (Extended Events)Spill eventsAny sustained rate

Fixes

Bad plan with oversized grant

If one query requests dramatically more memory than peers, the plan is the problem. Pull the plan from sys.dm_exec_query_plan and look for sort or hash operators with bad estimates. Update statistics on the underlying tables. Test with OPTION (RECOMPILE) to confirm a fresh plan helps. For stable fixes, use OPTION (MIN_GRANT_PERCENT, X) or OPTION (MAX_GRANT_PERCENT, Y) query hints to bound the grant. Both hints require SQL Server 2016 SP1 or later.

On SQL Server 2019 and later with database compatibility level 150 or higher, row mode Memory Grant Feedback adjusts future grants automatically based on past executions. On SQL Server 2022, percentile and persistence modes make feedback more accurate and stable, but require Query Store in read-write mode to take effect.

On SQL Server 2025, feedback is replica-aware for availability group secondaries.

Too many concurrent memory-intensive queries

When many queries legitimately need grants at the same time, the bottleneck is concurrency. If you have Enterprise Edition, Resource Governor workload groups can cap per-query grants with REQUEST_MAX_MEMORY_GRANT_PERCENT and isolate workloads into separate pools. Without Resource Governor, schedule heavy reporting or ETL jobs in separate windows, lower MAXDOP for the largest queries to reduce per-query grant size, or split large sorts into smaller batches.

Max server memory too low

If the OS has available memory but SQL Server is capped below what the workload needs, raise max server memory. Leave several GB for the OS, more on hosts with high RAM, and verify with sys.dm_os_process_memory that physical_memory_in_use_kb tracks the new setting. Do not raise it above physical RAM. Lock Pages in Memory should be enabled for the SQL Server service account on dedicated hosts to prevent working set paging.

Resource Governor cap too restrictive

If only one workload group shows the problem, check REQUEST_MAX_MEMORY_GRANT_PERCENT for that group. A cap of 25% combined with several concurrent memory-intensive queries will queue them. Raise the cap or reclassify the workload. Resource Governor is Enterprise Edition only. On Standard Edition, query hints and scheduling changes are the available levers.

External OS memory pressure

If system_memory_state_desc reports low memory, find the co-tenant. VM balloon drivers, backup agents, monitoring agents, and co-located SQL Server instances are common offenders. Adjust max server memory on each SQL Server instance so the sum leaves the OS headroom. On Linux, adjust the SQL Server OOM score so the engine is not the first process killed under pressure.

Prevention

  • Track Memory Grants Pending as a time series. A single point sample misses sustained pressure that begins and resolves between samples.
  • Track RESOURCE_SEMAPHORE wait deltas every 30 to 60 seconds. Cumulative wait stats since startup hide current problems.
  • Enable Memory Grant Feedback where supported. Row mode on SQL Server 2019, percentile and persistence on SQL Server 2022 with Query Store read-write.
  • Capture sort and hash warnings via Extended Events. Spills are silent unless you instrument them.
  • Maintain statistics freshness. Outdated statistics are the single largest driver of bad cardinality estimates and oversized grants.
  • Pre-size TempDB. If spills happen, TempDB needs the IOPS and space to absorb them without becoming its own bottleneck.

How Netdata helps

  • Per-second Memory Grants Pending collection catches the moment queries start queuing, before users notice.
  • Correlating Memory Grants Pending with RESOURCE_SEMAPHORE wait deltas confirms whether the queue is growing or resolving.
  • TempDB space and I/O metrics alongside grant pressure show whether the symptom has shifted from memory wait to spill I/O.
  • PLE and buffer cache hit ratio trends distinguish query workspace pressure from buffer pool pressure.
  • OS-level memory metrics (available memory, process_physical_memory_low) identify whether the cause is internal to SQL Server or external at the OS.
  • Anomaly detection on these correlated signals surfaces pressure that does not yet cross a static threshold.

Netdata’s Microsoft SQL Server monitoring with Netdata brings these signals together with per-second metrics and ML anomaly detection.