Esperas CMEMTHREAD e MemObj

Nos últimos posts, falamos sobre o Memory Clerk e o MemObj.

Monitorando Memória com os Clerks
https://blogs.msdn.com/b/fcatae/archive/2014/02/25/monitorando-memoria.aspx

Usando os Memory Objects
https://blogs.msdn.com/b/fcatae/archive/2014/03/05/memoria-memory-object.aspx

Nesse artigo vou utilizar um post do Bob Dorr sobre o CMemThread

How It Works: CMemThread and Debugging Them
https://blogs.msdn.com/b/psssql/archive/2012/12/20/how-it-works-cmemthread-and-debugging-them.aspx

Operador new

O SQL Server, assim como grande parte dos programas escrito em C++, utiliza o operador new para criar objetos.

obj = new CClass( );

Essa rotina é transformada em uma chamada a um objeto MemObj. Podemos dizer que esse comando é equivalente a:

obj = memObj.AllocateMemory( sizeof(CClass) );

// Seguido pela chamada ao construtor da classe

Como cada MemObj pertence a uma classe de Memory Clerk (sempre assim), então é possível sumarizar o consumo total de memória usando a DMV sys.dm_os_memory_clerks. A muito grosso modo, podemos dizer que o Memory Clerk é o somatório de toda memória utilizada pelo MemObj.

Memory Objects (MemObj)

Existem objetos MemObj dedicados por sessão, objeto, processador, etc. Em outras palavras, o SQL Server utiliza uma grande quantidade de MemObj em seu processo. Quer confirmar? Rode o comando:

select * from sys.dm_os_memory_objects

image 

Observe que foram retornados 686 MemObj diferentes! Os objetos MemObj utilizam “page allocators”, que são as interfaces de acesso ao Memory Clerk – semelhante a um proxy.

Alocação de Memória Single-Threaded

Apesar de existir múltiplos MemObj, há uma restrição de acesso em paralelo. Todos esses objetos são single-threaded e serializam a chamada de alocação por memória. Se um MemObj for compartilhado entre sessão, então observamos a “contenção por MemObj” e será visível ao administrador de sistema.

select * from sys.dm_exec_requests

image

No exemplo, observamos que uma das sessões está alocando memória (55), enquanto que há sessões esperando (56, 57, 58, 59, 60).

Famoso CMEMTHREAD

A origem do nome CMEMTHREAD é decorrente de um lock interno do SQL Server. Utilizando um dump de memória, podemos observar o problema em detalhe. Note que na linha 08 existe uma referência a CMemThread<CMemObj>.

image

Se fosse possível ler o código do SQL Server, então haveria algo assim:

CMemThread<CMemObj>::Alloc( size )
{
if( inUse ) {
wait( CMEMTHREAD );
}

latch_get ();

CMemObj.internal_allocate_memory (size);

latch_release ();
}

Trace Flag 8048

A chance de ocorrer uma contenção de memória no objeto MemObj é mínima, mas sempre existe a possibilidade de isso acontecer e impactar diretamente o desempenho da instância.

Existem 3 tipos de distribuição de MemObj:

  • 1 global
  • 1 por NUMA Node
  • 1 por CPU lógico

Uma forma de aliviar a contenção é criar objetos MemObj por CPU. Nesse caso, fica quase impossível haver concorrência de acesso. Esse comportamento é habilitado através do Trace Flag 8048.

SQL Server 2008/2008 R2 on Newer Machines with More Than 8 CPUs Presented per NUMA Node May Need Trace Flag 8048
https://blogs.msdn.com/b/psssql/archive/2011/09/01/sql-server-2008-2008-r2-on-newer-machines-with-more-than-8-cpus-presented-per-numa-node-may-need-trace-flag-8048.aspx