1

Como reduzi o uso de memória do Spotify em segundo plano sem fechar seus processos

O Spotify é um daqueles aplicativos que muita gente deixa aberto durante o dia inteiro. O problema é que, mesmo quando sua interface fica em segundo plano ou minimizada, seus processos continuam ativos.

Em vez de simplesmente encerrar esses processos, decidi explorar outra abordagem: controlar quanto de memória física eles podem manter residente.

Foi dessa ideia que surgiu o Memory Sentry, uma ferramenta de gerenciamento adaptativo de memória para o Spotify Desktop no Windows, integrada ao Spicetify.

Como ele fica na prática

Memory Sentry funcionando dentro do Spotify, mostrando a composição de memória por função, controles individuais e o Gerenciador de Tarefas do Windows.

Essa captura mostra uma execução com oito processos do Spotify, todos com limites aplicados. O painel registra 68,6 MB na composição de memória, enquanto o Gerenciador de Tarefas mostra 69,9 MB para o grupo Spotify.

Não é um benchmark comparativo nem uma promessa de resultado para qualquer computador. É um exemplo de como o painel apresenta os processos individualmente, em vez de tratar o Spotify como uma única entrada no Gerenciador de Tarefas.

Também é possível visualizar quais processos estão em modo adaptativo e quais possuem limites manuais.

Por que trabalhar com working set?

Primeiro, vale explicar o conceito que está no centro do projeto.

O working set de um processo é o conjunto de páginas de memória que atualmente estão residentes na memória física. São páginas que podem ser utilizadas sem precisar provocar uma falha de página para serem carregadas novamente.

Isso não é a mesma coisa que memória virtual comprometida ou o total de memória privada alocada por um processo.

O Windows disponibiliza a API SetProcessWorkingSetSizeEx, que permite estabelecer limites mínimo e máximo para o working set de outro processo.

É justamente essa API que o servidor do Memory Sentry utiliza.

Na prática, o objetivo é estabelecer um teto para a memória residente de cada processo, permitindo que o Windows gerencie a paginação de acordo com esses limites.

Documentação: SetProcessWorkingSetSizeEx — Microsoft Learn.

Um detalhe da implementação: a política adaptativa utiliza a medição normal do working set. Já o painel prefere apresentar o private working set quando essa informação está disponível para todos os processos monitorados, recorrendo ao working set convencional quando necessário.

O Spotify não é tratado como um processo único

O cliente desktop possui diferentes processos, cada um com uma responsabilidade.

O Memory Sentry identifica a árvore de processos do Spotify e classifica suas instâncias em funções como:

  • Main: processo principal.
  • Audio: processos relacionados à reprodução de áudio.
  • GPU: processamento gráfico.
  • Renderer: renderização da interface.
  • Utility: serviços auxiliares.
  • Crashpad: tratamento de falhas.
  • Helper: outros processos auxiliares.

Essa classificação é importante porque aplicar o mesmo limite indiscriminadamente a todos eles não seria uma boa estratégia.

Um processo de áudio, por exemplo, merece tratamento diferente de um processo de manutenção.

A implementação específica do Spotify fica isolada em um perfil próprio, enquanto a lógica de política de memória e as operações do Windows ficam separadas em outros módulos Rust.

PID não é a identidade do processo

Existe uma distinção importante na arquitetura.

O Windows precisa de um PID para localizar uma instância de processo e executar operações sobre ela. O Memory Sentry também utiliza o horário de criação do processo para evitar confundir duas instâncias que receberam o mesmo PID em momentos diferentes.

Mas os PIDs não são utilizados como identificadores persistentes das configurações por função.

Para isso, o projeto utiliza identidades construídas a partir da função e de um subidentificador, quando disponível.

Por exemplo:

  • Renderer:#7
  • Utility:network
  • Main
  • Audio

Essas identidades são utilizadas nas configurações de limites manuais, tetos adaptativos e perfis aprendidos.

Os PIDs ficam associados à instância real que está sendo monitorada. Inclusive, quando existem limites ativos, o servidor guarda um registro de recuperação com PID, horário de criação e limites originais do processo.

Isso permite recuperar a configuração anterior sem confundir processos diferentes.

O limite não é simplesmente um número fixo

Um dos motivos para não utilizar um teto fixo para todos os processos é que o consumo muda conforme o que o Spotify está fazendo.

O Memory Sentry começa com um teto automático de referência de 38 MB quando não existe um limite aprendido anteriormente, mas esse valor pode mudar durante a execução.

O motor acompanha algumas informações:

  • Histórico recente do working set.
  • Utilização de CPU.
  • Frequência de falhas de página.
  • Pressão de memória do sistema.
  • Histórico de utilização de cada identidade.

O histórico de demanda utiliza um histograma com decaimento exponencial e estima o percentil 95 do consumo observado.

Assim, uma oscilação pontual não precisa determinar sozinha o próximo limite.

A política também possui uma margem de segurança, intervalos mínimos entre ajustes e limites específicos para determinadas funções. O áudio, por exemplo, possui um piso automático de 25 MB. Renderizadores também recebem um tratamento especial quando a janela continua visível em segundo plano.

Outro mecanismo importante é a recuperação contra situações de restrição excessiva. Quando aparecem sinais de que um processo está precisando de mais memória, como falhas de página elevadas ou atividade de CPU com um conjunto residente muito pequeno, o limite pode ser aumentado.

A intenção é encontrar um equilíbrio entre reduzir a memória residente e não prejudicar o funcionamento do aplicativo.

E se alguma coisa der errado?

Essa é uma das partes que considero mais importantes do projeto.

Antes de modificar os limites de um processo, o servidor consulta os valores originais através de GetProcessWorkingSetSizeEx.

O registro de recuperação é salvo localmente antes de aplicar a alteração. Se não for possível persistir esse registro, a primeira alteração não é realizada.

Quando o Spotify volta ao primeiro plano, os limites originais são restaurados. O mesmo mecanismo participa dos caminhos de desligamento do servidor e da recuperação na inicialização.

Se o servidor for interrompido enquanto existem limites ativos, o registro deixado em disco permite tentar restaurar os valores originais na próxima execução.

Esse cuidado é especialmente importante porque o PID sozinho não é suficiente para identificar uma instância de processo: o Windows pode reutilizá-lo depois que um processo termina.

Um modo mais agressivo também está disponível

O comportamento padrão trabalha com os limites de working set, sem solicitar uma limpeza adicional a cada alteração.

Mas existe uma opção mais agressiva.

Nesse modo, o servidor também pode chamar EmptyWorkingSet, uma API que solicita ao Windows a remoção do maior número possível de páginas do working set do processo.

Essa operação não é gratuita: quando o processo precisar novamente dessas páginas, poderá ter que carregá-las outra vez.

Por isso, deixei esse comportamento como uma opção separada, em vez de torná-lo obrigatório.

Documentação: EmptyWorkingSet — Microsoft Learn.

Como a arquitetura está organizada

O projeto possui duas partes principais.

A interface é uma Custom App do Spicetify, escrita em JavaScript. Ela apresenta os processos, a composição de memória, os controles e os diagnósticos.

O servidor é escrito em Rust e executa o monitoramento e as operações nativas do Windows.

A comunicação entre os dois acontece através de uma API local vinculada a 127.0.0.1, protegida por um token específico da instalação.

Essa separação permite que o monitor continue trabalhando sem depender de a interface estar aberta dentro do Spotify.

O código também separa a política central de gerenciamento, a integração específica do Spotify e as operações Win32.

Atualmente, o foco é o Spotify Desktop no Windows 10 e Windows 11.

Código aberto

O Memory Sentry está disponível no GitHub:

yanlvl99/memory-sentry

Ainda há bastante espaço para testar o comportamento em diferentes máquinas, especialmente computadores com pouca memória disponível.

Gostaria de receber feedback de quem trabalha com gerenciamento de memória no Windows, desenvolvimento em Rust ou integração de aplicações com Spicetify.

Mais do que conseguir o menor número possível no Gerenciador de Tarefas, quero entender até onde é possível reduzir a memória residente sem comprometer a experiência de utilização.

Carregando publicação patrocinada...