PHP在循环中使用反射类时占用过多内存的解决方法

PHP在循环中使用反射类时占用过多内存的解决方法,php,memory,memory-management,reflection,memory-leaks,Php,Memory,Memory Management,Reflection,Memory Leaks,我有一个这样的环 foreach ($classes as $class) { $reflectionClass = new \ReflectionClass($class); ... /*code that doesn't matter - commenting it out leaves the memory consumption all the same */ } 这导致了致命错误:允许内存大小为134

我有一个这样的环

    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 MB内存设置),则会导致内存耗尽

循环开始前的内存使用量约为1.6MB

正如您可能猜到的,将
unset($reflectionClass)
放在循环体的末尾一点帮助都没有

通过谷歌搜索,我猜PHP不会释放对象占用的内存,以防它有一些对其他对象的内部引用。 现在,这些帖子(,)让我尝试使用垃圾收集器:

    gc_enable();
    foreach ($classes as $class)
    {
        $reflectionClass = new \ReflectionClass($class);
        ...
        unset($reflectionClass);
        gc_collect_cycles();
    }
这仍然会导致相同的结果

我看到的解决方案是: 1) 增加允许的内存设置-这是丑陋和悲伤的。 2) 将类分成多个部分,并通过分叉或执行其他一些PHP脚本分别从每个部分获得所需的结果——但这听起来很难做到

内存泄漏是否有简单的解决方法?我是不是遗漏了什么

更新

多亏了Paul Crovella指出,保留加载的类定义会占用内存,事实上这不是泄漏


因此,它可以通过在子进程中执行或通过另一个脚本来解决。添加了我自己的解决方案作为答案。

通过将部分类的处理委托给子进程实现了一种变通方法,子进程在退出时将所有加载的类定义带到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]);
        }
    }

通过将类的一部分的处理委托给子进程实现了一种变通方法,子进程在退出时将所有加载的类定义带到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]);
        }
    }

我无法重现这个问题。您使用的是什么版本的PHP?版本是5.4.15。奇怪的是,它对您来说是不可复制的,我一直在为每个循环增加+30-130KB,这取决于类内容。等等。您确定这是由于反射而导致的泄漏,而不仅仅是加载这些类定义的正常成本吗?它们在你循环之前加载了吗?这是一个很好的观点,谢谢!在循环的开头,只存在一个包含类名的字符串数组。按名称创建一个
ReflectionClass
肯定会加载类定义,但后者(或不应该)是否会被卸载,特别是在取消设置使用它的对象之后?不,类定义不会被卸载,也不应该被卸载。如果每次有人想要另一个对象时,自动加载器都必须返回磁盘读取,这将变成一场i/o噩梦。类定义是应用程序自然权重的一部分,它们不是漏洞。我无法重新创建此问题。您使用的是什么版本的PHP?版本是5.4.15。奇怪的是,它对您来说是不可复制的,我一直在为每个循环增加+30-130KB,这取决于类内容。等等。您确定这是由于反射而导致的泄漏,而不仅仅是加载这些类定义的正常成本吗?它们在你循环之前加载了吗?这是一个很好的观点,谢谢!在循环的开头,只存在一个包含类名的字符串数组。按名称创建一个
ReflectionClass
肯定会加载类定义,但后者(或不应该)是否会被卸载,特别是在取消设置使用它的对象之后?不,类定义不会被卸载,也不应该被卸载。如果每次有人想要另一个对象时,自动加载器都必须返回磁盘读取,这将变成一场i/o噩梦。类定义是应用程序自然权重的一部分,它们不是漏洞。