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
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
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>.
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