За последние 24 часа нас посетил 49241 программист и 1758 роботов. Сейчас ищут 768 программистов ...

Утечка памяти при использовании массива объектов

Тема в разделе "Прочие вопросы по PHP", создана пользователем apuzanoff, 9 июл 2015.

  1. apuzanoff

    apuzanoff Новичок

    С нами с:
    9 июл 2015
    Сообщения:
    4
    Симпатии:
    0
    Столкнулся при экспорте большого количества файлов с проблемой утечки памяти при работе с массивами объектов.
    Упрощенно код выглядит так:
    Код (PHP):
    1. class Test_Class
    2. {
    3.     private $a = null;
    4.  
    5.     public function __construct($a = null)
    6.     {
    7.         $this->a = $a;
    8.     }
    9.  
    10.     public function __destruct()
    11.     {
    12.         unset($this->a);
    13.     }
    14. }
    15.  
    16. print 'Memory before: '.memory_get_usage(1).' <br>'; // 262 144 
    17. $a = [];
    18. for ($i=0; $i<600000; $i++)
    19.     $a[] = new Test_Class($i);
    20.  
    21. print 'Memory after create: '.memory_get_usage(1).' <br>'; // 129 761 280 
    22.  
    23. for($i=0; $i < count($a); $i++)
    24.     unset($a[$i]);
    25.  
    26. unset($a);
    27.  
    28. print 'Memory after: '.memory_get_usage(1).' <br>'; // 35 389 440 
    29.  
    при очередной итерации память всё же заканчивается.
    Есть идеи как освободить занятую память?

    PHP, JavaScript, SQL и другой код пишите внутри тегов
    Код ( (Unknown Language)):
    1. [b]php][/b]Тут код[b][/[/b][b]code][/b][/color]
     
  2. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    xdebug есть? Протрассируйте расход памяти им. PHP вполне может "на всякий случай" оставлять себе "страничку" в размере 25% от максимального расхода, чтобы компенсировать возможные скачки производительности, например.
     
  3. apuzanoff

    apuzanoff Новичок

    С нами с:
    9 июл 2015
    Сообщения:
    4
    Симпатии:
    0
    xdubug умеет показывать чем именно занята память?
    Я пользуюсь другим отладчиком, в данном скрипте вижу что переменных у меня не осталось, но чем занята память он мне показать не может.
    25% от максимального расхода это нормально, я бы с этим согласился, но он сжирает память на каждой подобной итерации, и при генерации очередного файла скрипт обваливается
     
  4. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    То есть каждый раз добавляется 35 метров "мусора"?

    Добавлено спустя 6 минут 3 секунды:
    Умеет трассировать ее расход по времени и вызовам функций.

    Попробуй без деструктора.
     
  5. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Тоже сталкивался на больших объемах, принудительные unset`ы всего чего можно, деструкты и даже вызов gc не помогли. Память всё равно текла и на каком-то моменте резко падала производительность. Решил путем разбития на задачи: скажем, каждый процесс обрабатывает один файл и в зависимости от имеющихся ресурсов их можно стартовать либо по очереди, либо параллельно. Проблема перешла из области оперативки в область нагрузки на процессор, но это уже не так страшно ))
     
  6. apuzanoff

    apuzanoff Новичок

    С нами с:
    9 июл 2015
    Сообщения:
    4
    Симпатии:
    0
    Ну это и мой дебагер может. Проблема в том что память заполняется данными, данные выгружаются в файл, потом вроде как всё что можно/нужно я делаю unset, но память не освобождается.

    Да, тоже пробовали по-разному: с деструкторами, без них, с присваиванием null в разных вариациях, с вызовом gc_collect_cycles - результат близок к нулю. Тоже сейчас склоняемся к костылю, который будет для этой сотни экспортов запускать сотню экземпляров скрипта, но это же ужас...
     
  7. VLK

    VLK Старожил

    С нами с:
    15 дек 2013
    Сообщения:
    3.010
    Симпатии:
    58
    !!!!!!!!!!! может это как то поможет, может за место unset использовать в деструкторе:
    Код (PHP):
    1. $this->a = null; 
    я где то видел, на одну переменную ссылаются две переменные (по ссылке), делается unset а по ссылкам все равно данные доступны, а вот если = null то все = null.
     
  8. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Я воспользовался http://symfony.com/doc/current/components/process.html, позволяет довольно удобно рулить этими экземлярами и запускать их асинхронно, после чего можно сделать while(true) и внутри контролировать происходящее, добивая процессы до нужного количества, собирая вывод в массивчик ну и потом все в лог для разбора.

    Других вариантов побороть утечки не нашел, если найдете альтернативный, напишите. Очень интересно )
     
  9. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    Код (Text):
    1. $a = new SplFixedArray(600000);
    существенно уменьшает второй memory_get_usage(1).

    но в целом ситуацию с освобождением всей памяти не решает, к сожалению.
    думаю, дело в том что остается неосвобожденной память использованная самим менеждером памяти, гдето внутри себя. для технических нужд, так сказать. т.е. данные то успешно уничтожаются в конце, но остаются некие метаданные. зачем и почему - хз.
    но проблема в том, что пхп не дает нам встроенного решения.
     
  10. apuzanoff

    apuzanoff Новичок

    С нами с:
    9 июл 2015
    Сообщения:
    4
    Симпатии:
    0
    Второй memory_get_usage не представляет проблемы - на одну такую итерацию памяти гарантированно хватает, проблема в том, что таких итераций больше сотни, и рано или поздно хвосты, остающиеся от уничтоженных массивов сжирают всю память.

    Видимо будем резать скрипт на части и запускать внутренний блок через cURL
     
  11. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Это и есть классическая утечка. Автор, а не с объектами, если простые массивы гонять, тоже течет? Чисто из интереса - это у самого пыха аллокатор кривой, или это проблемы его ООП-движка.
     
  12. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    А ни кто не пробовал погонять на эту тему php7? Там вроде как обещают переписать всё это дело, ускорить производительность раза в полтора и всё такое )
     
  13. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Простой вопрос - у вас действительно случилась ошибка нехватки памяти, или вы просто циферки посмотрели и испугались?
     
  14. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    А разве он вышел уже?
     
  15. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Судя по постам, таки все падает в процессе.
     
  16. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Да, я посмотрел по коду PHP - падаем, когда real_usage не хватает. Хотя на самом деле память не кушается если смотреть данные по процессу (grep VM /proc/<pid>/status). Там по ходу она даже не течет, а какой-то косяк со счетчиком. Или же фича. В общем, причина не видна. Топикстартеру просто советую поднять лимит и посмотреть на реальное использование памяти.

    К слову, если его пример обернуть в цикл, то в какой-то момент у меня real_usage перестает расти. В общем баг в багтрекере есть уже с год, но движения по нему нет. 7.0 работает нормально.