PHP - почему при передаче по ссылке используется одинаковый объем памяти? - PullRequest
1 голос
/ 23 января 2020

Я получаю «память исчерпана» в моем PHP скрипте. Я не могу понять, почему. Предел составляет 128 МБ. Я отправляю из javascript xmlrequest () строку json размером не более 10 МБ (обычно менее 1 МБ). Сам сценарий PHP имеет размер 43 КБ. Даже копируя все дюжину раз, я не должен исчерпывать память. Я делаю несколько обращений к базе данных в getParentFolders (), но это производит только несколько строк. Конечно, не те 62 мегабайта, которые я использую. Я использовал Xdebug (см. Рисунок), но это не говорит мне ничего полезного, только то, что да, я использую много памяти.

Так что на данный момент я пытаюсь применить «лучшие практики», чтобы минимизировать память Применение. Простое исправление, как я думал, заключалось в передаче значений по ссылке. Поэтому я ставлю '&' перед каждой переменной в каждой функции. К моему удивлению, потребление памяти не изменилось. На самом деле, было несколько хуже на несколько байтов. Я также пытался использовать каждую переменную как глобальную, но опять же, к моему удивлению, разницы было мало.

Так что же происходит? Почему переход по ссылке и использование глобальных переменных не дает ожидаемого выигрыша в производительности? (см. изображения)

Xdebug 'проход по значению'

enter image description here

Xdebug "проход по ссылке"

enter image description here

Обратите внимание, что они в значительной степени идентичны.

Для тех, кто хочет получить код, есть функция getParentFolders (), которая возвращает только короткую строку, но каким-то образом использует 70Mb!

function getParentFolders(&$node) {  //returns a string of folders
    debugprint("GetParentFOlders()");  //prints if $DEBUG flag is on
    $parent = getParent($node);
    $path = "";
    while ($parent) {  //goes till we hit root folder
        $path = $parent->title . '/' . $path;  //prepend it
        $parent = getParent($parent);
    }
    return $path;
}

function getParent(&$node) {  //return node that is the parent 
    global $email;
    $parentId = $node->parentId;
    $clientId = $node->parentClient;
    $idCol = $clientId . "_id";
    $tablename = $email . "_bookmarks";
    $query = "SELECT * FROM `$tablename`
                        WHERE $idCol = '$parentId'";  //only return one row since id is unique
    $result = sendquery($query);
    return (object) $result[0];
}

Редактировать:

Просто чтобы уточнить, я ищу техническое объяснение PHP использования памяти и лучшие практики - в частности, почему я не вижу различий в памяти - нет Обойти эту проблему.

Ответы [ 3 ]

0 голосов
/ 25 января 2020

Значение $node является ссылкой, то есть указателем на объект. В PHP 5+ «объекты» не являются значениями напрямую. Вы всегда работаете с объектами через указатели на них: когда вы делаете new ClassName, он оценивает указатель на объект; когда вы используете оператор ->, он принимает указатель на объект с левой стороны.

Так что, если обе эти функции были переданы по значению, размер передаваемого объекта равен только размеру указателя. Когда вы передаете по ссылке, это, по сути, оборачивает указатель на другой уровень указателя, который имеет тот же размер (хотя общий объем памяти больше, поскольку имеется два указателя). Единственная точка передачи по ссылке - это то, что она позволяет присваивать $node внутри функции указатель на другой объект или содержать значение другого типа и отражать его в переданной переменной в вызывающей функции, но Вы не назначаете $node где-либо здесь (изменение поля объекта, указанного на $node не то же самое, что присвоение $node), поэтому передача по ссылке здесь бессмысленна.

(Кроме того, даже если вы передали тип значения, такой как массивы в PHP, они имеют семантику копирования при записи и не копируются до тех пор, пока вы не напишите в него. И даже если вы записали в него и заставили его сделать copy, эта копия будет действовать только в течение времени жизни этой локальной переменной, которая заканчивается в конце функции, поэтому она не будет создавать постоянное использование памяти.)

0 голосов
/ 04 февраля 2020

Ответ в том, что объекты не могут быть переданы по значению. Они всегда передаются по ссылке. Оператор 'pass by reference', '&', не будет делать ничего, кроме как обернуть ссылку на объект (т.е. указатель) в оболочку, а затем передать этот новый указатель.

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

0 голосов
/ 23 января 2020

Убедитесь, что результат запроса возвращает одну строку, используя LIMIT 1; Если результат не найден, сделайте возврат getParent() false или null, чтобы его можно было установить на $parent для выхода из while l oop. И я не думаю, что вам нужно передать аргумент $node по ссылке в вашем случае.

...