O que acontece com um programa quando você roda ele
Quando executamos um programa o mesmo permanece na memória RAM como um processo, mas a coisa é mais complicada do que a maioria dos manuais ensina. Você clica no executável, o sistema operacional cria uma estrutura de controle, aloca espaço de endereçamento virtual, e pronto — seu programa está rodando. Mas isso não significa que tudo fica parado na memória do jeito que você vê no Gerenciador de Tarefas.Um processo é basicamente um container que o kernel monta para isolar seu programa dos outros. Cada processo tem seu próprio espaço de endereçamento virtual, suas próprias variáveis de ambiente, descritores de arquivo abertos, e uma pilha de execução. O que você vê no gerenciador como "memória dedicada" é só a ponta do iceberg.
quando executamos um programa o mesmo permanece na memória: o que realmente acontece
O processo fica dividido em segmentos. Tem o texto, que é o código compilado em si. Tem o heap, onde malloc e new alocam memória dinâmica. Tem a pilha, com frames de função, argumentos, variáveis locais. E tem o espaço de dados, onde variáveis globais e estáticas vivem. Tudo isso é endereçamento virtual, o que significa que o sistema pode mapear páginas físicas da RAM sob demanda, e muitas vezes páginas que parecem estar aí na verdade estão só no disco de paginação até alguém acessá-las. Aqui vai algo que ninguém conta: um processo pode ocupar 500 MB no gerenciador de tarefas mas estar usando talvez 50 MB de RAM física real. O resto é memória mapeada, páginas compartilhadas, ou espaço reserved sem commit. No Windows isso é especialmente confuso porque o Gerenciador de Tarefas mostra Working Set Private (o que é exclusivo daquele processo) e Working Set (que inclui páginas compartilhadas). Se você não distinguir essas duas coisas, vai entender errado o quanto seu programa realmente gasta.
Eu tive um problema há uns anos com uma aplicação que supostamente "vazava memória". O processo crescia linearmente até estabilizar em torno de 2 GB e parar. A princípio parecia um leak clássico, então eu passei uma semana coletando heap dumps, analisando com o Process Explorer, rodando ferramentas de profiling. Nada. O comportamento era consistente, reproduzível, mas não mostrava alocações penduradas. O problema real era cache de memória do runtime. A aplicação usava um framework que mantinha estruturas em memória para performance, e elas nunca eram liberadas durante a execução porque o ciclo de vida delas estava atrelado ao processo inteiro. Quando eu configurei o GC para fazer collections mais agressivas e ativei logging de heap, percebi que o pico real de objetos vivos era muito menor do que o working set indicava. A workaround foi implementar um pooling manual de objetos e ajustar os thresholds do garbage collector. O uso de RAM caiu de 2 GB para algo em torno de 300 MB estáveis.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Outra coisa que as pessoas ignoram: processos podem ter threads, e threads compartilham o espaço de memória do processo mas têm pilhas separadas. Um programa com muitas threads pode parecer normal em uso de memória mas estar gastando CPU em troca de contexto o tempo todo. No meu caso específico, a aplicação tinha threads de background que criavam objetos de serialização em cada request e jamais liberavam referências por causa de closures presas ao ciclo de vida do thread. Vazamento disfarçado de consumo legítimo. Se você quer diagnosticar isso na prática, o primeiro passo é parar de confiar no Gerenciador de Tarefas ou no Activity Monitor. Use tools específicas: no Linux tem o smem que mostra memória compartilhada vs privada de forma mais honesta, no Windows o Process Explorer com a coluna Working Set (Private) faz mais sentido, e para análise profunda heap dumps com ferramentas como the .NET Memory Profiler, Valgrind, ou address sanitizer quando estiver compilando.
Processos também podem ser swapped para o disco. Se o sistema estiver sob pressão de memória, páginas inativas vão para o swap. Quando o programa acessa essas páginas de novo, ocorre um page fault e elas voltam da memória. Isso não é visível no gerenciador comum — você vê o processo como "ativo" mas o I/O do disco vai disparar. É uma fonte comum de lentidão que ninguém conecta com uso de memória. O limite prático é que processos nunca somem da memória até serem finalizados. Mesmo processos "congelados" ou em pause continuam alocados. E em sistemas com contêineres ou sandboxing, cada container é essencialmente um processo com restrições impostas por namespaces e cgroups, o que adiciona overhead de memória que não aparece no seu processo individual.
Se seu programa precisa rodar por dias ou semanas sem reinicialização, monitore o working set private ao longo do tempo, não o pico inicial. Um crescimento lento e constante de algumas páginas por hora pode significar semanas até virar problema. E sempre teste com carga real, não com dados sintéticos pequenos, porque patterns de uso afetam dramaticamente como a memória se comporta em produção.