Есть ли способ узнать, какие объекты и сколько их у меня в памяти?
У меня есть php-скрипт, который использует Doctrine2 и Zend для вычисления некоторых вещей из базы данных и отправки некоторых электронных писем для 30.000 пользователей.
В моем скрипте происходит утечка памяти, и я хочу знать, какие объекты потребляют эту память, и, если возможно, кто хранит ссылку на них (таким образом, не позволяя им быть освобожденными).
Я использую php 5.3.x, поэтому простые циклические ссылки не должны быть проблемой.
Я пытался использовать возможности трассировки xdebug, чтобы получить mem_delta без успеха (слишком много данных).
Я попытался вручную добавить memory_get_usage до и после важных функций. Но единственный вывод, который я сделал, заключался в том, что я теряю около 400 тысяч на пользователя и 3000 пользователей, что дает мне 1 Гб, который у меня есть.
Существуют ли какие-либо другие способы узнать, где и почему происходит утечка памяти? Спасибо
3 answers
Вы можете попробовать отправить, скажем, 10 электронных писем, а затем вставить это
get_defined_vars();
Http://nz.php.net/manual/en/function.get-defined-vars.php
В конце сценария или после отправки электронного письма (в зависимости от того, как настроен ваш код).
Это должно сказать вам, что все еще загружено, и что вы можете отменить/превратить в ссылку.
Также, если загружено много двух вещей, вы получаете это близко к началу и концу своего кода и выясняете разницу.
30.000 объектов для гидратации - это довольно много. Доктрина 2 стабильна, но есть некоторые ошибки, поэтому я не слишком удивлен вашими проблемами с утечкой памяти.
Хотя с меньшими наборами данных я добился некоторого успеха, используя возможности пакетной обработки доктрин и создавая повторяющийся результат.
Вы можете использовать код из примеров и добавлять gc_collect_cycles()
после каждой итерации. Вы должны проверить это, но для меня размеры партий около 100 или около того работали довольно хорошо – это число обеспечил хороший баланс между производительностью и использованием памяти.
Очень важно, чтобы скрипт распознавал, какие объекты обрабатываются, чтобы его можно было перезапустить без каких-либо проблем и возобновить нормальную работу без повторной отправки электронных писем.
$batchSize = 20;
$i = 0;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
while (($row = $iterableResult->next()) !== false) {
$entity = $row[0];
// do stuff with $entity here
// mark entity as processed
if (($i % $batchSize) == 0) {
$em->flush();
$em->clear();
gc_collect_cycles();
}
++$i;
}
В любом случае, возможно, вам следует немного пересмотреть свою архитектуру для этого сценария, так как ORM не очень хорошо подходит для обработки больших объемов данных. Может быть, вам сойдет с рук работа с необработанными строками SQL?
Это не тот инструмент, который даст вам то, что вам нужно, но, возможно, он вам поможет. Если вы еще этого не сделали, вы можете реализовать шаблон карты идентификации, в котором каждый раз, когда вы создаете объект, он регистрируется на карте идентификации, поэтому в любое время вы можете позвонить в службу мгновенных сообщений и посмотреть, какие объекты загружены, или попросить ее выгрузить любые загруженные объекты.