Tenho um servidor com dois scripts PHP rodando continuamente em segundo plano. Um desses scripts, por algum motivo, não está liberando memória e está fazendo o outro script falhar, pois, eventualmente, não tenho mais RAM disponível no servidor.
Criei uma POC bem simples para você verificar por conta própria. Basicamente, tenho uma função que verifica a memória RAM disponível (usando um free
comando) três vezes e, após a primeira vez, executo a função que faz com que a memória seja consumida e nunca liberada. Eu até tentei, gc_collect_cycles
mas nada acontece.
O que me impressiona é que, após o script terminar completamente, se eu acessar meu SSH e executar "free", toda a memória usada será liberada. No entanto, se eu adicionar um sleep longo no final do meu script, a memória continuará sendo usada. Observe que, dentro da função, test()
uma variável local é criada e explicitamente desativada, mas, por algum motivo, o PHP ainda retém a memória.
<?php
set_time_limit(0);
ini_set("memory_limit","-1");
function show_available_ram() {
$temp1111 = shell_exec('sudo free -m');
preg_match("/Mem: +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +([0-9]+)/",$temp1111,$temp2222);
return $temp2222[1] . " MB";
}
function test() {
$temp3333 = array();
for ($i=0;$i<1000000;$i++) {
$temp3333[] = array("key1" => md5($i), "key2" => $i);
}
unset($temp3333);
}
echo "\nAAA: " . show_available_ram();
test();
sleep(2);
echo "\nBBB: " . show_available_ram();
//I know it should not be needed the line below, but I am trying it anyway but it does not help.
gc_collect_cycles();
sleep(2);
echo "\nCCC: " . show_available_ram();
?>
O código acima gera:
AAA: 1297 MB
BBB: 873 MB
CCC: 869 MB
Assim, você pode ver claramente a memória sendo usada (em BBB
e CCC
mas não liberada mesmo depois de test()
já ter terminado.
No código acima, usei alguns sleep
para que o PHP tenha tempo de coletar lixo, mas isso não ajuda em nada.
A memória é limpa somente quando todo o script PHP termina de ser executado e o processo PHP é encerrado . É nesse momento que o sistema operacional recupera toda a memória alocada para esse processo. É por isso que você vê a memória liberada quando remove o comando
sleep()
e o script é concluído rapidamente.Uma solução para o problema é dividir o processo em partes. Com base no script de exemplo que você forneceu, abaixo está o script atualizado que executa o trabalho em lotes.