Esperas CMEMTHREAD e MemObj


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

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

Usando os Memory Objects
http://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
http://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
http://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

Comments (5)

  1. Fala Gafa!

    Fantástico esse post, principalmente porque se vê conceitos (poucos) por ai, mas não há uma demonstração de como seria no código. Quando e entende o conceito isso ajuda bastante a galera ter uma visão diferente das coisas.

    Parabéns!

    Um abraço

  2. Obrigado Alberto! Se tiver novas ideias ou tópicos me avise.  

  3. Thiago Caserta disse:

    Cara, sensacional! O código exemplo deixou as coisas muito mais claras mesmo.

    Parabéns pelo post!

    Um abraço,

  4. Junior Galvão - MVP disse:

    Show, agora eu consegui entender bem melhor o CMEMTHREAD!!!!

Skip to main content