Aumente a performance da sua aplicação em .NET escolhendo o modo de trabalho do Garbage Collector

Tiago Tartari
4 min readOct 1, 2020

O Garbage Collector tem dois “sabores” de operações que se entendido e usado de forma correta, permite melhorar a performance de aplicações .NET consideravelmente.

Nesse post falo quais são os modos de operação do Garbage Collector, Workstation e Server.

Em primeiro lugar precisamos entender o que é o Garbage Collector, mesmo que de forma básica, uma vez que esse artigo trata de outro assunto.

O GC é responsável por remover da memória objetos sem uso. Ele trabalha de forma não deterministica, ou seja, não somos nós programadores quem determinamos como ele vai coletar algo, o GC é inteligente o suficiente para determinar isso, embora há um certo nível de configuração que possamos fazer, mas exige muita experiência e geralmente naquilo que usamos o .NET a configuração inicial do GC, ou seja, sem customização, resolve a maior parte dos nossos problemas.

O responsável por gerenciar o GC é o CLR, Common Language Runtime, é nele que tudo acontece. O legal é que não ficamos no escuro, o CLR oferece alguns contadores para que possamos entender as atividades do GC, no perfmon podemos procurar por .NET CLR Memory.

Configurações da minha máquina

Machine NameTIAGOTARTARIOperating SystemWindows 10 EnterpriseOS Build Number17763.1.x86fre.rs5_release.180914–1434UTC offset where data was collected-3,00UTC offset where PerfView is running-3,00Delta of Local and Collection Time0,00OS Boot Time07/17/2020 17:40:32.500Trace Start Time07/18/2020 20:45:27.599Trace End Time07/18/2020 20:45:58.501Trace Duration (Sec)30,9CPU Frequency (Mhz)2.200Number Of Processors4Memory Size (Meg)16.298Pointer Size8Sample Profile Interval (MSec)1,00Total Events141.907Lost Events0ETL File Size (MB)35,4No data collection log file found

Modo Workstation, para minimizar suspensões

Este modo é o padrão. Aplicação Desktop utiliza o modelo Workstation, isto porque, este modo prioriza minimizar atrasos para uma melhor experiência da interface.

A thread do GC é lançada junto a thread do usuário, inclusive com a mesma prioridade. Sempre será utilizada o modo Workstation para computadores com um processador, mesmo que você tente alterar por meio de configurações.

Algumas características são importantes nesse modo, pelo fato de segmentos menores, veremos acionamentos de Garbage Collector mais frequentes, minimizando suspensões.

Conseguimos confirmar o modo Workstation utilizando o WinDbg.

0:000> !EEVersion 
4.700.20.26901 (3.x runtime) free
4,700,20,26901 @Commit: 018cfd06dceb19b6eb1e9217a500fb1071946fcd
Workstation mode
SOS Version: 3.0.1.2901 retail build

Também conseguimos confirmar, que o modo Workstation disponibiliza somente uma Heap e somente um segmento de memória. Para sistemas 32-bits, no modo Workstation, o tamanho do segmento será de 16 Mb e para sistemas de 64-bits o tamanho do segmento será de 256 Mb, caso o seguimento fique cheio um novo será criado.

0:000> !EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x00000189DA42F8C8
generation 1 starts at 0x00000189D9CAEFB0
generation 2 starts at 0x00000189D8921000
ephemeral segment allocation context: none
segment begin allocated size
00000189D8920000 00000189D8921000 00000189DA45A7C0 0x1b397c0(28547008)
Large object heap starts at 0x00000189E8921000
segment begin allocated size
00000189E8920000 00000189E8921000 00000189E8925CB8 0x4cb8(19640)
Total Size: Size: 0x1b3e478 (28566648) bytes.
------------------------------
GC Heap Size: Size: 0x1b3e478 (28566648) bytes.

Modo Server, para aumentar o throughput

Aconselhada, com ressalva, o uso para aplicações em servidor, como ASP.NET Core ou Web API, que necessitam de escala e melhor troughput.

Este modo precisa ser configurado, utilize <gcServer enabled="true"/> para aplicações em .NET Core e para aplicações Full Framework <gcServer enabled="true"/>.

No modo Server as coletas do Garbage Collector ocorrem em várias threads dedicados, que estão com o nível de prioridade THREAD_PRIORITY_HIGHEST.

Conseguimos confirmar o modo Server utilizando o WinDbg.

0:000> !EEVersion
4.700.20.26901 (3.x runtime) free
4,700,20,26901 @Commit: 018cfd06dceb19b6eb1e9217a500fb1071946fcd
Server mode with 4 gc heaps
SOS Version: 3.0.1.2901 retail build

O resultado mostra algo interessante, no modo Server para computadores com 4 processadores ou mais teremos uma heap e uma thread dedicada para cada processador. Podemos confirmar com WinDbg.

0:000> !EEHeap -gc
Number of GC Heaps: 4
------------------------------Heap 0 (0000025579066980)
generation 0 starts at 0x000002557ABFFEE0
generation 1 starts at 0x000002557AA21018
generation 2 starts at 0x000002557AA21000
ephemeral segment allocation context: none
segment begin allocated size
000002557AA20000 000002557AA21000 000002557B9B7830 0xf96830(16345136)
Large object heap starts at 0x000002597AA21000
segment begin allocated size
000002597AA20000 000002597AA21000 000002597AA21018 0x18(24)
Heap Size: Size: 0xf96848 (16345160) bytes.
------------------------------Heap 1 (0000025579069B70)
generation 0 starts at 0x000002567ABF9B70
generation 1 starts at 0x000002567AA21018
generation 2 starts at 0x000002567AA21000
ephemeral segment allocation context: none
segment begin allocated size
000002567AA20000 000002567AA21000 000002567B98CB98 0xf6bb98(16169880)
Large object heap starts at 0x000002598AA21000
segment begin allocated size
000002598AA20000 000002598AA21000 000002598AA462E8 0x252e8(152296)
Heap Size: Size: 0xf90e80 (16322176) bytes.
------------------------------Heap 2 (0000025579108140)
generation 0 starts at 0x000002577ABA9BF8
generation 1 starts at 0x000002577AA21018
generation 2 starts at 0x000002577AA21000
ephemeral segment allocation context: none
segment begin allocated size
000002577AA20000 000002577AA21000 000002577B9488F0 0xf278f0(15890672)
Large object heap starts at 0x000002599AA21000
segment begin allocated size
000002599AA20000 000002599AA21000 000002599AA21850 0x850(2128)
Heap Size: Size: 0xf28140 (15892800) bytes.
------------------------------Heap 3 (00000259C515FE90)
generation 0 starts at 0x000002587ABE2670
generation 1 starts at 0x000002587AA21018
generation 2 starts at 0x000002587AA21000
ephemeral segment allocation context: none
segment begin allocated size
000002587AA20000 000002587AA21000 000002587B94B958 0xf2a958(15903064)
Large object heap starts at 0x00000259AAA21000
segment begin allocated size
00000259AAA20000 00000259AAA21000 00000259AAA21018 0x18(24)
Heap Size: Size: 0xf2a970 (15903088) bytes.
------------------------------
GC Heap Size: Size: 0x3d7a178 (64463224) bytes.

Os segmentos, geralmente, no modo Server são maiores onde em sistemas de 32-bits temos 64 Mb e para 64-bits 4 Gb, entretanto, para computadores com mais de 4 processadores lógicos para 32-bits temos 32 Mb e para 64-bits 2 Gb, para computadores acima de 8 processadores para 32-bits temos 16 Mb e para 64-bits 1 Gb.

O modo Server permite que as coletas sejam mais rápidas, justamente pelas várias threads fornecidas. Aqui está o razão para aplicativos com o ASP.NET ou WebApi podem ter um troughput menor.

Por quê o modo Server deve ser utilizado com ressalva?

A utilização do modo Server, pode usar mais recursos. Caso você tenha mais aplicações no IIS, por exemplo, considere utilizar o modo Workstation para obter uma melhor performance.

Na prática, se bem utilizado o modo em que o Garbage Collector trabalha, teremos boa performance.

No próximo post, vamos abordar a utilização do modo em que o Garbage Collector opera em containers e um comparativo de uma aplicação funcionando no modo Workstation e Server, confesso que ficarão perplexos.

--

--

Tiago Tartari

Microsoft MVP, programador por mais de 18 anos onde 10 deles atuando como arquiteto de soluções para e-commerce, palestrante técnico, apaixonado por performance