Обходной путь для PHP, занимающего слишком много памяти при использовании класса Reflection в цикле

php memory memory-management reflection memory-leaks

338 просмотра

1 ответ

У меня есть такая петля

    foreach ($classes as $class)
    {
        $reflectionClass = new \ReflectionClass($class);
        ... /*code that doesn't matter - commenting it out leaves the
              memory consumption all the same */
    }

Что приводит к неустранимой ошибке: допустимый размер памяти в 134217728 байт исчерпан, что приводит к тому, что большое количество классов проходит через цикл и относительно небольшой допустимый объем памяти в php.ini (для меня это около 4000 классов и 128 МБ памяти).

Использование памяти перед началом цикла составляет около 1,6 МБ .

Как вы можете догадаться, оставление unset($reflectionClass)в конце тела цикла не помогает вообще.

После некоторого поиска в Google, я предполагаю, что PHP не освобождает память, занятую объектом, в случае, если у него есть внутренние ссылки на другие объекты. Теперь эти посты ( один , два , три ) заставили меня попытаться использовать сборщик мусора точно:

    gc_enable();
    foreach ($classes as $class)
    {
        $reflectionClass = new \ReflectionClass($class);
        ...
        unset($reflectionClass);
        gc_collect_cycles();
    }

Что до сих пор приводит к тому же результату.

Решения, которые я вижу: 1) Увеличьте допустимую настройку памяти - что некрасиво и грустно. 2) Разделите классы на порции и получите необходимый результат от каждой порции отдельно, используя разветвление или выполнение какого-либо другого сценария PHP - но это звучит как трудный путь.

Есть ли простой обход утечки памяти? Я что-то здесь упускаю?

Обновить

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

Так что это может быть решено либо выполнением в дочернем процессе, либо другим скриптом. Добавил мое собственное решение в качестве ответа.

Автор: BVengerov Источник Размещён: 08.11.2019 11:17

Ответы (1)


0 плюса

Решение

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

Выглядит так:

    foreach (array_chunk($classes, 300) as $classesPortion)
    {
        if (socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $socketArray) === false)
            throw new \Exception('Could not create socket');

        $pid = pcntl_fork();
        if ($pid === -1) //Forking failed
        {
            throw new \Exception('Could not fork process');
        }
        elseif ($pid === 0) //Is child process
        {
            socket_close($socketArray[1]);
            foreach ($classesPortion as $class)
            {
                $data = ...; //generating needed data
            }
            $dataString = serialize($data);
            if (!socket_write($socketArray[0], $dataString, strlen($dataString)))
            {
                throw new \Exception('Failed to write to socket');
            }
            socket_close($socketArray[0]);
            exit(0);
        }
        else //Is parent process
        {
            socket_close($socketArray[0]);
            pcntl_waitpid($pid, $childProcessStatus);
            if ($childProcessStatus !== 0)
            {
                throw new \Exception('Child process exited abnormally');
            }
            else
            {
                $result = socket_read($socketArray[1], 1000000, PHP_BINARY_READ);
                ... // deal with result
            }
            socket_close($socketArray[1]);
        }
    }
Автор: BVengerov Размещён: 09.10.2016 05:04
Вопросы из категории :
32x32