致命错误 - 打开的文件过多

39

我尝试在新电脑上运行PHPUnit测试,但出现以下错误:

PHP致命错误:未捕获的异常 'UnexpectedValueException',信息为'RecursiveDirectoryIterator ::__construct(/usr/lib/php/pear/File/Iterator):打开目录失败:打开的文件太多'(位于/usr/lib/php/pear/File/Iterator/Factory.php的第114行)

旧电脑上相同的代码可以正常运行...

新电脑环境: PHP版本:PHP 5.3.21(cli) 旧版本:PHP 5.3.14

PHPUnit每次的输出结果:

................EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE 65 / 66 ( 98%)
E

Time: 34 seconds, Memory: 438.50Mb

There were 50 errors:

1) XXXXXXXXXXX
PHP Fatal error:  Uncaught exception 'UnexpectedValueException' with message 'RecursiveDirectoryIterator::__construct(/usr/lib/php/pear/File/Iterator): failed to open dir: Too many open files' in /usr/lib/php/pear/File/Iterator/Factory.php:114

2
你是否忘记调用 fclose 函数? - h2ooooooo
fclose?抱歉,我不理解您的建议。 - Mauro
10个回答

53

这可能是代码运行所在服务器的限制。每个操作系统只允许打开一定数量的文件/句柄/套接字。当服务器被虚拟化时,这个限制通常会进一步减少。在 Linux 服务器上,您可以使用 ulimit -n 命令检查当前限制,如果您具有 root 访问权限,可以使用相同的命令增加它。我想 Windows 服务器也有相应的方法。否则,除了请求主机或管理员增加限制之外,没有太多可做的。

更多可配置限制:

/etc/security/limits.conf 中进行更改。

soft nofile 1024
hard nofile 65535

通过ulimit -n 65535echo 65535 > /proc/sys/fs/file-max或在/etc/sysctl.conf中增加ulimit:

fs.file-max=65535

5
$ ulimit -n > 256。我在OSX上。 - Mauro
1
@Mauro 我在Mavericks(OSX 10.9)上得到了4864。 - JamesHalsall
@JamesHalsall 我现在正在使用1024。 - Mauro
2
在我的情况下,无论我将 ulimit -n 设置多高(我甚至尝试了 1,000,000),这个问题都会发生。问题最终被证明是 PHP 类中的错误逻辑导致的。值得一提的是,我正在使用 Laravel,并且有一个工厂状态返回非标量值而没有使用闭包语法;我通过使用闭包语法来修复了这个问题,然后 ulimit -n 设置为 2048 就足够了。 - Ben Johnson

35

如何提高文件打开限制(适用于Linux或Mac OS):

ulimit -n 10000

解决使用phpunit或/和phpdbg时出现的问题:警告:Uncaught ErrorException: require([..file]): failed to open stream: Too many open files in [...]


它只针对登录用户有效,如果您以root身份登录,则您的root获取ulimit 10000,但不是您的php mysql服务。 - Kamil Dąbrowski

5
在 PHP 中,在执行之前,尝试执行以下操作。
exec('ulimit -S -n 2048');

1
这对我不起作用。尝试在setUp()setUpBeforeClass()方法中都添加了这行代码。但在启动单元测试之前手动在终端上执行它确实有效。 - Koen
@Koen 我也是这样。我猜 PHP 进程没有权限在操作系统级别上更改这个。 - Pelmered

3

从睡眠模式“唤醒”计算机后,我遇到了这个问题。

像这样重新启动php-fpm可以解决它。经典的重启电脑再试一次的解决方案。

sudo /etc/init.d/php-fpm restart

我认为这可能与我最近添加到php中的xdebug有关。


2

不要将DirectoryIterator对象存储到以后使用; 当您存储超过操作系统限制(通常为256或1024)时,您将收到错误消息"文件打开太多"。

例如,如果目录中有太多文件,则会产生错误:

<?php 
$files = array(); 
foreach (new DirectoryIterator('myDir') as $file) { 
    $files[] = $file; 
} 
?>

据推测,这种方法也需要大量的内存。来源:http://php.net/manual/pt_BR/directoryiterator.construct.php#87425

1

在使用Http池时,我遇到了一个错误,因为我将太多的URL添加到了池中(大约有2000个URL)。

我不得不将这些URL分成较小的批次,然后错误就消失了。

我认为这是Guzzle池的工作原理,它在整个池完成之前不会关闭curl连接。

例如:

$responses = Http::pool(function (Pool $pool) use ($chunk) {
    return collect($chunk)->map(fn($url) => $pool->get($url));
});

变为:

collect($urls)
    ->chunk(25)
    ->each(function ($chunk) {
        $responses = Http::pool(function (Pool $pool) use ($chunk) {
            return collect($chunk)->map(fn($url) => $pool->get($url));
        });
    });

Http函数是Laravel使用Guzzle Http Client的包装器函数。https://laravel.com/docs/9.x/http-client


1

也许,您的/etc/init.d/phpx.x-fpm文件存在一些错误。 让我们重新启动它:

sudo /etc/init.d/php7.2-fpm restart

1

我注意到在PHP中,当你忘记将某些内容包装在闭包中时,会出现这种情况。仔细查看最近的差异,你可能能够找到问题的根源(在我的情况下,我在Laravel PHP单元测试工厂中引用了$faker,但没有使用闭包)。


1
在Debian服务器上,你也可以去到。

/etc/php/php7.xx/fpm/pool.d/www.conf

rlimit_files = 10000
/etc/init.d/php7.xx restart

0
我遇到了一个错误,每次 PHP 尝试加载 Redis 库时都会出现这个错误,但这是由于一些我最初没有想到的东西引起的。当我的程序运行一段时间并进行重复处理时,我不断收到这个错误。我发现我打开了一个 cURL 会话($ch = new curl_init(...)),它在类的析构函数中关闭,但是那个析构函数从未被调用。我解决了这个问题,这个太多文件打开的错误就消失了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接