Gerenciamento de memória e coleta de lixo
Veja como funcionam zvals, contagem de referências e o GC.
Gerenciamento de memória e coleta de lixo é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.
Como o PHP Gerencia a Memória
Os valores do PHP ficam armazenados em estruturas zval, gerenciadas principalmente por contagem de referências, com um coletor de ciclos para detectar o que a contagem de referências não consegue detectar. Compreender isso explica vazamentos de memória em processos de longa duração, o custo das cópias e quando gc_collect_cycles() é importante.
O zval
Um zval é o contêiner de qualquer valor PHP: uma etiqueta de tipo e uma união para armazenar o valor. Os escalares (int, float, bool) são armazenados por valor dentro do zval e não usam contagem de referências (NOT). Os tipos complexos (cadeia de caracteres, matriz, objeto) apontam para uma estrutura com contagem de referências na memória dinâmica.
<?php
// debug_zval_refcount-style inspection via gc + count
$arr = [1, 2, 3];
$copy = $arr; // refcount of the array bumps, no data copy yet (CoW)
// Modifying triggers copy-on-write separation
$copy[] = 4;
var_dump($arr); // still [1,2,3]
var_dump($copy); // [1,2,3,4]
?>Contagem de Referências
Cada valor com contagem de referências registra quantas estruturas zval apontam para ele. Atribuir incrementa a contagem; usar unset ou sair do escopo a decrementa. Quando a contagem chega a zero, a memória é liberada imediatamente — sem esperar uma etapa do coletor. A maior parte da memória do PHP é recuperada dessa forma.
<?php
$a = str_repeat('x', 1000); // refcount 1 on the string
$b = $a; // refcount 2
unset($a); // refcount 1
unset($b); // refcount 0 -> freed instantly
echo "Refcounting frees deterministically\n";
?>Cópia na Escrita
A atribuição não copia os dados; ela os compartilha e aumenta a contagem de referências (cópia na escrita). A duplicação real só ocorre na primeira escrita em um valor compartilhado. Isso torna barato passar matrizes grandes até que sejam modificadas.
<?php
$big = range(1, 100000);
$shared = $big; // O(1): shared, refcount++
// Reads stay shared and cheap
echo count($shared), PHP_EOL;
// First write separates (copies) — now it costs O(n)
$shared[0] = -1;
echo $big[0], ' vs ', $shared[0], PHP_EOL; // 1 vs -1
?>O Problema: Ciclos
A contagem de referências, sozinha, não consegue liberar ciclos de referências. Se o objeto A mantiver uma referência para B e B mantiver outra para A, as contagens nunca chegam a zero, mesmo depois que todas as variáveis externas forem removidas — a memória vaza até que o coletor de ciclos seja executado.
<?php
class Node { public ?Node $ref = null; }
$a = new Node();
$b = new Node();
$a->ref = $b; // a -> b
$b->ref = $a; // b -> a (cycle!)
unset($a, $b); // external refs gone, but internal cycle keeps refcount > 0
// The objects are unreachable but NOT yet freed by refcounting alone.
echo gc_collect_cycles() . " cycles collected\n";
?>O Coletor de Ciclos
O coletor de ciclos do PHP implementa marcação e varredura síncronas sobre raízes. Possíveis raízes de ciclos (valores cuja contagem de referências foi decrementada, mas não chegou a zero) são armazenadas em uma área temporária. Quando essa área fica cheia (o padrão é 10.000), o coletor é executado: ele simula decrementos, encontra grupos realmente inacessíveis e os libera.
<?php
// GC status exposes the root buffer and run stats
for ($i = 0; $i < 3; $i++) {
$a = new stdClass();
$b = new stdClass();
$a->b = $b; $b->a = $a; // create a cycle
unset($a, $b); // becomes a GC root
}
print_r(gc_status());
?>Controlando o Coletor
É possível controlar o coletor: gc_disable()/gc_enable() o ativam ou desativam, e gc_collect_cycles() força uma etapa e retorna o número de itens liberados. Em tarefas em lote críticas para a vazão, às vezes as pessoas desativam o coletor durante uma etapa intensiva e o executam uma vez ao final.
<?php
gc_disable(); // no automatic cycle collection
// ... heavy phase that creates and drops many cycles ...
for ($i = 0; $i < 1000; $i++) {
$x = new stdClass(); $y = new stdClass();
$x->y = $y; $y->x = $x; unset($x, $y);
}
$freed = gc_collect_cycles(); // reclaim all at once
gc_enable();
echo "Freed $freed objects\n";
?>Processos de Longa Duração
Em processos CLI de longa duração, consumidores de filas e processos de trabalho do Swoole/RoadRunner, a memória não é recuperada pelo encerramento da requisição — o processo permanece ativo por horas. Observe: caches estáticos que crescem sem limite, ciclos que se acumulam e recursos não fechados. Monitore com memory_get_usage(true) e reinicie os processos periodicamente.
<?php
$peakStart = memory_get_usage(true);
// Simulate processing a batch of jobs
for ($job = 0; $job < 5; $job++) {
$payload = range(0, 10000);
// ... process ...
unset($payload);
}
printf("start: %d KB, peak: %d KB, now: %d KB\n",
$peakStart >> 10,
memory_get_peak_usage(true) >> 10,
memory_get_usage(true) >> 10
);
?>WeakReference e WeakMap
Para armazenar dados em cache ou associá-los a um objeto sem mantê-lo ativo, use WeakReference (8.0) e WeakMap (8.0). Uma entrada de WeakMap não incrementa a contagem de referências do objeto usado como chave, portanto o objeto pode ser coletado e sua entrada removida automaticamente — ideal para tabelas auxiliares de metadados que não devem causar vazamentos.
<?php
$map = new WeakMap();
$obj = new stdClass();
$map[$obj] = 'metadata'; // does NOT keep $obj alive
echo count($map) . PHP_EOL; // 1
unset($obj); // object collectible -> entry removed
echo count($map) . PHP_EOL; // 0
?>Cadeias de Caracteres Internadas e Imutáveis
As cadeias de caracteres literais do seu código-fonte são internadas: armazenadas uma única vez, compartilhadas e nunca liberadas nem têm a contagem de referências alterada durante a requisição. O OPcache move as cadeias de caracteres internadas e os op_arrays imutáveis (armazenados em cache) para a memória compartilhada entre requisições. É por isso que copiar cadeias de caracteres literais tem custo praticamente zero.
Limite de Memória e Alocador
O PHP usa seu próprio alocador emalloc (Gerenciador de Memória Zend), sobreposto ao alocador do sistema, acompanhando o uso por requisição em relação a memory_limit. Atingir o limite gera um erro fatal. true em memory_get_usage(true) informa os blocos realmente alocados pelo OS, não apenas o que o seu script mantém atualmente.
<?php
echo 'limit: ' . ini_get('memory_limit') . PHP_EOL;
echo 'emalloc usage: ' . memory_get_usage() . " bytes\n"; // ZendMM tracked
echo 'real usage: ' . memory_get_usage(true) . " bytes\n"; // from OS
?>Verificação Rápida
Por que a contagem de referências, sozinha, pode causar vazamentos em alguns objetos?
Resumo
O PHP armazena os valores em estruturas zval; os escalares são armazenados por valor, enquanto os tipos complexos são alocados na memória dinâmica e têm contagem de referências com cópia na escrita. A contagem de referências libera a memória de forma determinística quando chega a zero, mas não consegue recuperar ciclos; por isso, um coletor síncrono de marcação e varredura é executado quando a área temporária de raízes fica cheia (ou por meio de gc_collect_cycles()). Use WeakMap/WeakReference para associações sem posse e observe o crescimento sem limite em processos de longa duração.
Perguntas Frequentes
A aula “Gerenciamento de memória e coleta de lixo” é grátis?
Sim — o texto completo de “Gerenciamento de memória e coleta de lixo” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.
O que vou aprender em “Gerenciamento de memória e coleta de lixo”?
Veja como funcionam zvals, contagem de referências e o GC. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar PHP Academy?
Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Gerenciamento de memória e coleta de lixo”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de PHP Academy?
Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Como funciona o Zend Engine
- Gerenciamento de memória e coleta de lixo
- OPcache e compilação JIT
- Escrevendo uma extensão PHP básica em C