file_get_contents жрёт много памяти

Fenslau

Новичок
Привет, комрады! Возникла проблема:
Мне нужно в цикле считать 300 000 раз file_get_contents.
url - внешний. каждое считывание примерно 140 кб и записывается в переменную. Далее происходит работа с этой переменной - и всё по новой. В эту же самую переменную. Старая информация от прошлых считываний нигде не хранится и не нужна.
Я заметил, что выделенная для процесса php память при этом линейно увеличивается с работой скрипта.
Сначала я пытался делать unset этой переменной, куда я считываю содержимое и много разных других копий сломал.
Потом решил сделать эксперимент - одно лишь изменение - url для file_get_contents - один и тот же и на локальном сервере. И всё - потребление памяти прекратилось.
По видимому эти данные от file_get_contents почему-то где-то помимо моей воли кэшируются(?) и я не знаю, как это запретить, или сбрасывать хотя бы при каждом прохождении цикла. Подскажите, как быть?
 

WMix

герр M:)ller
Партнер клуба
обычно пишут коротенький код, который репродуцирует ошибку.
 

Fenslau

Новичок
PHP:
for ($j=0; $j<10000; $j++) {                   
    $group_get = json_decode(file_get_contents('https://download.hdd.tomsk.ru/download/vvmeqcrt?354a757ca7ef6528924e4e0d7ad1ec89'), true);
echo '#####'.$j;   
}
если ссылку https... заменить на локальный файл (тот же самый), то память не разрастается. А так, каждые 1000 итераций +1 Мб примерно. Размер файла 127кб, в json_decode 500кб.
 

WMix

герр M:)ller
Партнер клуба
PHP:
for ($j=0; $j<10; $j++) {
    echo 'before:'. memory_get_usage(true)."\n";
    $group_get = file_get_contents('http://localhost');
    echo 'after:'. memory_get_usage(true)."\n";
}
Код:
before:262144
after:262144

before:262144
after:524288

before:524288
after:262144

before:262144
after:524288

before:524288
after:262144

before:262144
after:524288
у меня все ок, да чистка памяти затормаживает, но происходит
 

Fenslau

Новичок
Так эти цифры у меня тоже одинаковые всегда...
Разрастается сам объем памяти процесса php, который был выделен для этой задачи...
А эта цифра хоть и с параметром true показывает не его ведь....
за 1000 чтений из файла +1 МБ. за 100 000 +100МБ.
А если файл локальный, то ничего не растёт!
 

WMix

герр M:)ller
Партнер клуба
Разрастается сам объем памяти процесса php, который был выделен для этой задачи...
Возвращает количество памяти в байтах, которое было выделено PHP-скрипту на данный момент.
Передача TRUE позволяет узнать реальное количество памяти, выделенной PHP скрипту системой
я не понимаю что у тебя не работает, скрипт и данные в студию
 

Fenslau

Новичок

я не понимаю что у тебя не работает, скрипт и данные в студию
PHP:
<?php       
for ($j=0; $j<10000; $j++) {

echo 'before:'. memory_get_usage(true)."\n";
    $group_get = json_decode(file_get_contents('https://download.hdd.tomsk.ru/download/vvmeqcrt?354a757ca7ef6528924e4e0d7ad1ec89'), true);
echo 'after:'. memory_get_usage(true)."\n";

echo '#####'.$j."\n";   
}
?>
Вот это весь скрипт на данный момент! Остальное я просто выкинул - во избежании всяческих сомнений.
То что находится по адресу https... - реальный файл. Он там существует и считывается. Точно такую же информацию я считываю в рабочем скрипте.
Итак. Этот вот короткий скрипт из одного цикла и одной строчки в нём - на 1000 проходе цикла добавляет +1 Мб к процессу
9697 root 20 0 316052 29084 20676 S 1.0 2.9 0:23.54 php
вот именно сейчас он запущен
before:2097152
after:2097152
#####4364

Это то что он выдает на экран консоли. эти цифры 2097152 - одинаковые с начала старта этого скрипта.
Как видим, они мало соотносятся с объемом памяти, который ест весь процесс.
29084 - эта цифра (объем памяти процесса) - растёт хоть и медленно, но неуклонно. И к 100 000 итерации дорастает до +100Мб!
Вот.
Если же вместо https написать ссылку на локальный файл, то объем памяти процесса не разрастается вообще!!!
То есть при одном и том же скрипте разница только в том, дергаем ли мы файл извне, или берем с сервера. Поэтому я предполагаю, что эти данные не отдаются в систему, а нарастают и куда-то записываются, кэшируются или пёс знает, зачем он их собирает! Я хочу понять, как их освобождать сразу и вообще это чёртово кэширование не использовать. Гуглил по слову кэширование, но там что-то всё не то....
 

WMix

герр M:)ller
Партнер клуба
PHP:
for ($j=0; $j<10000; $j++) {
    echo "##### ".$j."\n";
    echo 'before:'. memory_get_usage()."\n";
    $group_get = file_get_contents('http://localhost');
    echo 'after:'. memory_get_usage()."\n";
    echo exec('ps -aux|grep "php -a"')."\n";
}
Код:
##### 0
before:226144
after:240224
wmix     14803  0.0  0.0 130308   880 pts/0    S+   16:18   0:00 grep php -a
...
##### 4499
before:240200
after:240200
wmix     21400  0.0  0.0 130308   880 pts/0    S+   16:20   0:00 grep php -a
 

Fenslau

Новичок
PHP:
for ($j=0; $j<10000; $j++) {
    echo "##### ".$j."\n";
    echo 'before:'. memory_get_usage()."\n";
    $group_get = file_get_contents('http://localhost');
    echo 'after:'. memory_get_usage()."\n";
    echo exec('ps -aux|grep "php -a"')."\n";
}
Код:
##### 0
before:226144
after:240224
wmix     14803  0.0  0.0 130308   880 pts/0    S+   16:18   0:00 grep php -a
...
##### 4499
before:240200
after:240200
wmix     21400  0.0  0.0 130308   880 pts/0    S+   16:20   0:00 grep php -a
http://localhost - здесь что и сколько килобайт в объеме?
почему бы не запустить с моим файлом?
 

WMix

герр M:)ller
Партнер клуба
там стандартная страничка от nginx

какая разница что там?
 

Fenslau

Новичок
там стандартная страничка от nginx

какая разница что там?
я уже третий раз пишу, что разница есть между локальными данными и теми, что грузятся извне...
если файл локальный, то процесс НЕ РАЗРАСТАЕТСЯ, проблемы нет. Только мне то нужен сторонний файл! И именно тогда возникает проблема.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
у меня не воспроизводится

PHP:
<?php
echo 'before:'. memory_get_usage(true)."\n";

for ($j=0; $j<200; $j++) {
    json_decode(file_get_contents('https://download.hdd.tomsk.ru/download/vvmeqcrt?354a757ca7ef6528924e4e0d7ad1ec89'), true);
    echo '.';
}
echo "\n".'after:'. memory_get_usage(true)."\n";
gri@User-pk:/mnt/c/www$ php test.php
before:2097152
........................................................................................................................................................................................................
after:2097152
gri@User-pk:/mnt/c/www$ php -v
PHP 7.3.4 (cli) (built: Apr 5 2019 15:14:11) ( NTS DEBUG )
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
В соседней консоли включил top, наблюдаю выделение/освобождение памяти на уровне до 100 килобайт, естественная работа менеджера памяти.

в начале цикла PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19906 gri 20 0 99132 16300 11136 S 1.0 0.1 0:00.10 php
в середине цикла:19906 gri 20 0 99264 16352 11132 S 1.0 0.1 0:00.63 php
в конце19906 gri 20 0 99264 16300 11068 S 0.0 0.1 0:01.37 php
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
че делать - выкинуть свой php или OS и проверять на нормальных сборках
 

Fenslau

Новичок
В соседней консоли включил top, наблюдаю выделение/освобождение памяти на уровне до 100 килобайт, естественная работа менеджера памяти.

в начале цикла PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19906 gri 20 0 99132 16300 11136 S 1.0 0.1 0:00.10 php
в середине цикла:19906 gri 20 0 99264 16352 11132 S 1.0 0.1 0:00.63 php
в конце19906 gri 20 0 99264 16300 11068 S 0.0 0.1 0:01.37 php
Я точно так же делал - включал топ и с ужасом наблюдал, как памяти процессу требовалось всё больше и больше. По началу конечно незаметно, но когда к концу дня накопилось 100 Мб.....
Причем такое поведение было как минимум в двух случаях - на разделяемом хостинге и на впс другом.
А те цифры, которые memory_get_usage даёт - у меня были одинаковыми на всем протяжении цикла....
А вы использовали тот мой код (в частности ссылку на файл?) Странно, может быть и правда пхп у меня не тот.
Но вообще, если кому интересно, я наметил некоторое решение.
Использую Curl вместо file_get_contents - и потребление памяти стоит на месте! Так что если в остальном с этим курлом проблем не будет, я просто для этого куска скрипта буду использовать его....
upd: заметил что вы поставили количество итераций 200...
Нужно минимум 10000 чтобы заметить эффект.
 

fixxxer

К.О.
Партнер клуба
В stream wrappers были утечки памяти. Но очень давно.
Какая версия PHP?

И еще, что будет, если тот же файл не по https:// забирать, а по http:// ? Могут быть утечки в openssl или с чем там собрано.

Никакого кэширования в http stream wrappers нет, это точно.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@Fenslau , чудес не бывает, если на 200 итерациях память возвращается с точностью до байта - на тысяче утечек тоже нет. Я могу даже докер-образ сделать, который будет воспроизводить миллион запросов, и выделение памяти будет идентично до байта. Проблема в вашей сборке.
Если в curl ошибки нет, только с wrapper - проблема в сборке. Как сказал fixxer, весьма вероятно, проблема в старой дырявой версии openssl. Сидеть пол-часа смотреть на 10 тысяч запросов, чтобы убедиться в этом мне влом.
В 10 тысяч http-запросов и консольный top на shared hosting я не верю - на хостинге такой процесс просто убьют по таймауту, и top никто не даст выполнить :)
 
Последнее редактирование:

Fenslau

Новичок
Я не исключаю проблемы в сборке. Мало того, возможно это вообще не баг, а какая-то фича. Возможно даже она настраивается каким-то способом в ini файле. Тем не менее, у меня память увеличивается не каждую итерацию по килобайту, а скачками. И на 100-200 может реально оставаться такой же. В среднем же по мегабайту за 1000.
Так или иначе, я заменил file_get_content на Curl - пока всё норм вроде! Посмотрим, что в конце дня будет.
PHP 7.0.33-0ubuntu0.16.04.5 (cli) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.33-0ubuntu0.16.04.5, Copyright (c) 1999-2017, by Zend Technologies
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
то, что твоя версия PHP мертва, ошибки в ней уже полтора года как не исправляются, а 16й убунте и ssl-библиотеке в ней уже 3 года - тебя не смущает

зачем решать проблему - проще поныть на форумах, накатать костыль и обвинить сам php
 
Последнее редактирование:
Сверху