Утечки памяти обычно не беспокоят PHP-разработчиков. Типичное приложение обрабатывает один запрос и работает не больше секунды. После этого вся использованная им память освобождается. Даже если приложение кушает слишком много, максимум, разработчик упирается в memory_limit
, выставленный хостером, что решить в общем случае довольно просто: как только переменная становится не нужна, очищаем память, занимаемую ей, при помощи unset
.
Однако, при выполнении ресурсоёмких задач (например, обработки большого количества данных) или запуске PHP как демона проблема утечек встаёт очень остро.
В PHP 5.2 нет полноценного сборщика мусора. Вместо него используется подсчёт ссылок.
Все значения переменных хранятся в памяти. И чтобы занимать как можно меньше места, переменные с одинаковыми значениями просто ссылаются на одну и ту же область памяти. При этом количество ссылок подсчитывается и, как только оно становится равно нулю, память освобождается.
$a = 10;
$b = $a;
unset($a);
$a = 10;
$b = $a;
$b = 1;
В PHP 5.2 причиной утечек являются циклические ссылки:
class A {
private $b;
function __construct(){
$this->b = new B($this);
}
}
class B {
private $a;
function __construct($a){
$this->a = $a;
}
}
$i=1;
while($i<=1000){
$a = new A();
unset($a);
echo $i."\t".memory_get_usage()."\n";
$i++;
}
Исправляется это явным уничтожением ссылки на B при помощи unset:
class A {
private $b;
function __construct(){
$this->b = new B($this);
}
function __destruct(){
unset($this->b);
}
}
class B {
private $a;
function __construct($a){
$this->a = $a;
}
}
$i=1;
while($i<=1000){
$a = new A();
unset($a);
echo $i."\t".memory_get_usage()."\n";
$i++;
}
В PHP 5.3 более умный сборщик мусора, который умеет находить и подчищать
последствия использования циклических ссылок. Однако,
поиск таких ссылок занимает
значительное время и зависит от количества «неподчищенных» ссылок. Плюс к этому работает
сборщик не постоянно, а срабатывает только при наполнении буфера ссылок. То есть до его срабатывания какое-то количество памяти всё-таки успевает утекать.
На заметку. Посмотреть, сколько памяти кушает ваше приложение можно при помощи следующих функций:
memory_get_usage()
— использованная скриптом память в байтах в момент вызова функции.
memory_get_usage(true)
— использованная скриптом и менеджером памяти PHP память в байтах в момент вызова функции.
memory_get_peak_usage()
— максимальное количество памяти в байтах, использованной скриптом с запуска скрипта до момента вызова функции.
memory_get_peak_usage(true)
— максимальное количество памяти в байтах, использованной скриптом и менеджером памяти PHP с запуска скрипта до момента вызова функции.