Php unset не освобождает память

Обновлено: 06.07.2024

У меня есть php-приложение, которое должно управлять (экспортировать) большим (огромным) объемом данных, и это должно быть сделано на производстве. поэтому мне нужно сделать как можно меньше использования памяти (основные критерии).

Короче говоря, приложение экспортирует данные в цикле, как

Я не хочу показывать здесь полную логику, потому что это может занять много времени у других народов, это не главное.

Главное, почему, если я буду unset() все вновь создаваемые переменные во время каждой итерации это не уменьшает использование памяти? как это

и как я мог уменьшить использование памяти? если мне нужно использовать так много запросов, переменных и т.д..

PS код не мой:) и я не хочу его переписывать с нуля, я просто ищу направление оптимизации.

без очистки переменных использование памяти будет следующим (оно измеряется в начале каждой итерации)

и с очисткой переменных

Существует инструмент под названием valgrind, он может помочь, пошел попробовать:)

Хотя unset() не освобождает память, потребляемую процессом PHP, она освобождает ее для использования самим сценарием PHP.

Итак, если вы создаете в цикле переменную размером 10M размером 10 раз и удаляете (или перезаписываете) ее в конце цикла, потребление памяти должно составлять 10M + все остальные переменные к концу цикла,

Если он растет - значит, где-то в полной логике есть утечка, которую вы не хотите показывать.

Потому что PHP делает это автоматически.
Когда переменная больше не используется, PHP сам ее отменяет.

Так как unset не освобождает память Это просто освобождает переменную. Но если эта переменная указывает на какую-то сложную структуру, то управление памятью в PHP не может понять, как освободить ее, это не так.

Кстати, твой foreach. unset loop в любом случае ничего не делает. PHP использует основанную на ссылках систему отложенной записи при записи для оптимизации использования памяти. Этот цикл foreach, по сути, не используется. Хранилище освобождается - оно возвращается внутреннему распределителю emalloc Zend Engines (не ОС) - или используется повторно, когда счетчик ссылок для любого элемента равен нулю. В любом случае это произойдет для локальных переменных, когда вы покидаете область действия функции, и для свойств класса, когда вы уничтожаете объект класса. Нет смысла клонировать поверхностную копию переменной, а затем сбрасывать ее, поскольку это просто делает +1 -1 на счетчике ссылок.

Если вы анализируете код для основных переменных, используемых в цикле, и сбрасываете их, тогда вы действительно уменьшите счетчик ссылок до 0, а затем освободите хранилище для повторного использования. Однако, как предполагает troelskyn, существует много способов, которыми существующий код может оставлять элементы данных с ненулевой ссылкой. Классический способ - если в вашем коде используются ссылки, вы можете создавать циклические цепочки ссылок, которые никогда не будут восстановлены, если цикл явно не нарушен. Даже наличие глобального массива, который используется для хранения результатов, может увеличить объем памяти.

Извините, но ваше заявление:

Я не хочу показывать здесь полную логику, потому что это может занять много времени у других народов, это не главное.

неправильно. В PHP, если вы хотите понять, почему вы не возвращаете хранилище в пул памяти, вы должны изучить код.

У меня есть приложение php, которое должно управлять (экспортировать) большой (огромный) объем данных, и это нужно делать при производстве. поэтому мне нужно сделать как можно меньше использования памяти (основные критерии).

Вкратце скажем, что приложение экспортирует данные в цикле, например

Я не хочу показывать полную логику, потому что это может занять много времени для других народов, это не главное.

Главное, почему, если я буду unset() всех вновь созданных переменных во время каждой итерации, это не уменьшит использование памяти? как это

и как я могу уменьшить использование памяти? если мне нужно использовать так много запросов, variebles и т.д.

P.S. код не мой:) и я не хочу переписывать его с нуля, я просто ищу направление оптимизации.

без использования переменных памяти чистка памяти (она измеряется в начале каждой итерации)

и с очисткой переменных

Есть инструмент, называемый valgrind, который может помочь, попробовал его:)

спросил(а) 2020-03-12T18:15:38+03:00 1 год, 8 месяцев назад

Хотя unset() не освобождает память, потребляемую процессом PHP, она освобождает ее для использования самим PHP script.

Итак, если вы создаете переменную 10M размером 10 раз в цикле и отключаете (или переписываете) ее в конце цикла, потребление памяти должно быть до 10M + все остальные переменные к концу цикла.

Если он растет - тогда есть утечка где-то в полной логике, которую вы не хотите показывать.

ответил(а) 2020-03-12T18:35:24.945066+03:00 1 год, 8 месяцев назад

Весь этот блок кода работает с копиями (или даже копиями) ваших переменных, поэтому он добавляет большие объемы памяти.
Все, что вы когда-либо снимали, это копии копий в цикле foreach.

Вам нужно отключить фактические переменные, которые вы используете

ответил(а) 2020-03-26T18:31:23.261925+03:00 1 год, 7 месяцев назад

Потому что PHP делает это автоматически.

Когда переменная больше не используется, PHP выдает ее сам.

ответил(а) 2020-03-12T18:15:38+03:00 1 год, 8 месяцев назад

Потому что unset не освобождает память. Он просто освобождает переменную. Но если эта переменная указывает на какую-то сложную структуру, это управление памятью PHP не может понять, как освободиться, это не будет.

ответил(а) 2020-03-12T18:15:38+03:00 1 год, 8 месяцев назад

unset() для переменной отмечает это для 'сборщика мусора'

Вы пробовали __destruct()?

ответил(а) 2020-03-12T18:15:38+03:00 1 год, 8 месяцев назад

Кстати, ваш foreach. unset loop ничего не делает. Для оптимизации использования памяти PHP использует основанную на ссылках систему запаздывания на основе записи. Этот цикл foreach это фактически не-op. Хранение освобождается - это возвращается внутреннему распределителю emaloc Zend Engines (не к ОС) - или повторное использование, когда счетчик ссылок для любого элемента равен нулю. Это произойдет в любом случае для локальных переменных, когда вы покидаете область действия функции и свойства класса при уничтожении объекта класса. Нет смысла клонировать неглубокую копию переменной, а затем отключать ее, так как это просто +1 -1 в счетчике ссылок.

Если вы передадите код для основных переменных, используемых в цикле, и отмените их, то вы действительно уменьшите количество отсчетов ссылок и до 0, затем вы освободите хранилище для повторного использования. Однако, как полагает troelskyn, существует много способов, которыми существующий код может оставлять элементы данных с ненулевой ссылкой. Классический способ заключается в том, что ваш код использует ссылки, чем вы можете создавать циклические цепочки ссылок, которые никогда не будут восстановлены, если цикл не будет явно нарушен. Даже наличие глобального массива, который используется для хранения результатов, может зависеть от памяти.

Извините, но ваше утверждение:

Я не хочу показывать полную логику, потому что это может занять много времени для других народов, это не главное.


неверно. В PHP, если вы хотите понять, почему вы не возвращаете память в пул памяти, вы должны изучить код.

Я понимаю, что второй вариант позволяет избежать накладных расходов на вызов функции, но было бы интересно узнать, какой лучше из них. Я использовал unset() на протяжении большей части моего кодирования, но недавно я просмотрел несколько приличных классов, найденных в сети, которые используют $var = null вместо этого. Есть ли предпочтительный вариант, и каково его обоснование?

Ответ 1

  1. К огда вы попытаетесь использовать несуществующую (не установленную) переменную, возникнет ошибка, а значением для выражения переменной будет null. (Потому что, что еще должен делать PHP? Каждое выражение должно приводить к какому-то значению.)

  2. Тем не менее, переменная с присвоенным ей null остается совершенно нормальной переменной.

Ответ 2

Выполняя unset() для переменной, вы, по сути, помечаете переменную для « сборки мусора » (в PHP его нет, но для примера), поэтому память становится недоступной. Переменная больше не содержит данных, но стек остается прежнего размера. При выполнении метода null данные исчезают , и память стека уменьшается почти сразу. Лично я использую unset() между итерациями в цикле, чтобы не было задержки, связанной с увеличением размера стека. Данные исчезают, но возникает задержка. На следующей итерации память уже занята php и, таким образом, быстрее инициализируется следующая переменная.

Ответ 3

  • Время выполнения 0.88389301300049 в секундах

  • Время выполнения 2.1757180690765 в секундах

  • Время выполнения 1.7235369682312 в секундах

  • Время выполнения 2.9490959644318 в секундах

  • Время выполнения 3.0069220066071 в секундах

  • Время выполнения 4.7002630233765 в секундах

  • Время выполнения 2.6272349357605 в секундах

  • Время выполнения 5.0403649806976 в секундах

Ответ 4

Что касается объектов, особенно в сценарии ленивой загрузки, следует учитывать, что сборщик мусора работает в «холостых» циклах процессора, поэтому предположение, что у вас возникнут проблемы, когда загружается много объектов, будет решаться освобождением памяти за счет небольшого временного штрафа. Используйте time_nanosleep, чтобы позволить GC освобождать память. Желательно установить переменную в null. Протестировано на рабочем сервере, первоначально задание потребляло 50MB и затем было остановлено. После использования nanosleep постоянное потребление памяти составило 14MB. Следует сказать, что это зависит от поведения GC, которое может меняться от версии PHP к версии. Но на PHP 5.3 это работает нормально. Например, этот пример:

for($n=0; $n<count($ids); $n++)

//unset($product); // для массивов

$product = null

if( $n % 50 == 0 )

// пусть GC управляет памятью

//echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];

time_nanosleep(0, 10000000);

>

$product = $productModel->getProductSingle((int)$ids[$n],true, true, true);

.

Ответ 5

Я все еще сомневаюсь в этом коде, но я попробовал его в своем скрипте и использую xdebug, чтобы узнать, как это повлияет на использование памяти моим приложением. Скрипт установлен для моей функции следующим образом:

function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts')

$sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";

if($showSql === FALSE)

$sql = mysql_query($sql) or die(mysql_error());

$data = mysql_fetch_array($sql);

return $data[0];

> else echo $sql;

>

И я добавляю unset прямо перед кодом возврата, и это дает мне 160200 циклов, затем я пытаюсь изменить его на $sql = NULL, и это дает мне 160224 циклов.

Но есть кое-что уникальное в этом сравнении, когда я не использую unset() или NULL, xdebug выдает мне 160144 циклов, как при использовании памяти.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

по идее объем используемой памяти до создания объекта и после его удаления должен совпадать: $beforeCreate = $afterDelete,
однако на деле получаем: $beforeCreate < $afterDelete.

Почему unset не освобождает память,выделенную под объект?
Как тогда освободить эту память?

Ненамного уменьшает память, тем не менее разница остается большая. и что так сильно много памяти расходуется? попробуйте несколько раз сделать тоже самое - подозреваю память расходуется только в первый раз yan писал(а): и что так сильно много памяти расходуется? попробуйте несколько раз сделать тоже самое - подозреваю память расходуется только в первый раз Нет, "утекающая" память накапливается после каждого обращения.
В итоге затрудняется работа с большими объемами данных.

Суть в том, что там нужно доставать Over 300 000 записей, естесвенно это все делается пачками, таким образом как-раз и планировалось избежать любых проблем с памятью.

Только беда в том, что когда в цикле достаем записи, они копятся в оперативной памяти.
unset переменных, в которые были получены данные ActiveRecord, не приводит ни к чему толковому.
Тоесть ссылка(переменная) удаляется, а сами данные остаются, и при этом копятся.

а нужны ли при таких объемах модели, может asArray использовать?

Да уж думали и так, и сяк.
Там нужен ActiveRecord именно)
Таковы данные задачи.

Нужно понять как чистить память от моделей.

Самое лучше, что вы можете сделать это unset($mailer);

Этот вызов означает, что $mailer удалится, но память не высвободится. Всё потому, что mailer это по сути ссылка на контейнер в памяти. На этот контейнер ссылаются Mailer атрибуты.

Всё само очистится, только когда скрипт завершится - по другому в php "утекающую память" не высвободить.

Переход на array не решит проблему утечки памяти, просто меньше будет её использоваться. Но утечка имеет место быть, при unset и ничего с этим не поделать :) Если вы упёрлись в лимит, то нужно изменять логику отдельных участков приложения.

Как вариант завершать процесс и создавать заново с нужной итерации. yiijeka писал(а): Переход на array не решит проблему утечки памяти, просто меньше будет её использоваться. Но утечка имеет место быть, при unset и ничего с этим не поделать с массивами все таки утечка намного меньше получается по опыту

vismut писал(а): Суть в том, что там нужно доставать Over 300 000 записей, естесвенно это все делается пачками, таким образом как-раз и планировалось избежать любых проблем с памятью.

Только беда в том, что когда в цикле достаем записи, они копятся в оперативной памяти.
unset переменных, в которые были получены данные ActiveRecord, не приводит ни к чему толковому.
Тоесть ссылка(переменная) удаляется, а сами данные остаются, и при этом копятся.

Помнится в книге PHP5 Котерова, была такая рекомендация: чтобы удалить переменную, то нужно было сначала присвоить ей пустое значение, а потом вызывать unset.

yiijeka писал(а): Самое лучше, что вы можете сделать это unset($mailer);

Этот вызов означает, что $mailer удалится, но память не высвободится. Всё потому, что mailer это по сути ссылка на контейнер в памяти. На этот контейнер ссылаются Mailer атрибуты.

Если бы дело было только в атрибутах класса.

Имея простейший класс:

все равно получаем утечку $beforeCreate < $afterDelete Потому что память расходовалась на внутренние механизмы new SomeClass;
По правильному нужно было Но там все равно будет разница. От этого не уйти. Никто, кроме самого php, не сможет очистить память. PHP выполняет очистку памяти автоматически. Вы не сможете явно удалить из памяти что-то. Вы сможете только поставить в очередь на удаление - это и есть unset() в вашем случае.

в принципе unset очищает память практически полностью, оставляя какие-то крохи байт где-то на поддерживающие механизмы (проверил на 5.5)
запустил в цикле $object = new SomeClass(); unset($object); память вообще ни как не растет по ходу цикла
кстати, присвоение null вообще ни как не помогает, хотя в предыдущих версиях может и помогала.

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

astronin писал(а): в принципе unset очищает память практически полностью, оставляя какие-то крохи байт где-то на поддерживающие механизмы (проверил на 5.5)
запустил в цикле $object = new SomeClass(); unset($object); память вообще ни как не растет по ходу цикла
кстати, присвоение null вообще ни как не помогает, хотя в предыдущих версиях может и помогала.

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

Читайте также: