PHP中出现“无法为池分配内存”的原因是什么?

133

我偶尔会遇到服务器的内存分配限制,特别是在使用像WordPress这样臃肿的应用程序时,但从未遇到“无法为池分配内存”的情况,并且难以追踪任何信息。

有人知道这是什么意思吗? 我尝试增加memory_limit,但没有成功。 我也没有对应用程序进行任何重大更改。 一天没问题,第二天就出现了这个错误。

12个回答

125

使用TTL值为0意味着当APC内存耗尽时,它将清除所有缓存。虽然这样可以消除错误,但会使APC的效率大大降低。这是一种“零风险、零麻烦、我不想做我的工作”的决定。APC并不适用于这种方式的使用。您应该选择足够高的TTL,以便最常访问的页面不会过期。最好的方法是提供足够的内存,使APC不需要清除缓存。

只需阅读手册就能了解ttl的使用:http://www.php.net/manual/en/apc.configuration.php#ini.apc.ttl

解决方案是增加分配给APC的内存。通过增加apc.shm_size来实现。

如果APC编译为使用共享段内存,则将受到操作系统的限制。输入此命令以查看每个段的系统限制:

sysctl -a | grep -E "shmall|shmmax"
为了分配更多的内存,您需要增加参数apc.shm_segments中的段数。
如果APC正在使用mmap内存,则没有限制。内存的大小仍由相同的选项apc.shm_size定义。
如果服务器上的内存不足,则使用过滤器选项来防止缓存较少访问的PHP文件。
但是永远不要使用TTL为0。
正如c33s所说,使用apc.php检查您的配置。将文件从apc包复制到Web文件夹并将浏览器指向它。您将看到实际分配和使用情况。图表在几个小时后必须保持稳定,如果它们每次刷新都完全改变,则意味着您的设置有问题(APC正在刷新所有内容)。将分配的内存比APC实际使用的多20%作为安全余量,并定期进行检查。
允许仅32MB的默认值太低了。PHP是在服务器具有64MB并且大多数脚本每个页面使用一个PHP文件的情况下设计的。现今的解决方案(例如Magento)需要超过10k个文件(~60Mb在APC中)。您应该允许足够的内存以便大多数php文件始终被缓存。这不是浪费,而是将opcode保留在RAM中比将相应的原始PHP保存在文件缓存中更有效率。现在,我们可以找到专用服务器,其内存高达24GB,每月仅需80美元左右,因此请不要犹豫,允许APC使用几GB的内存。我在托管5个Magento商店和~40个WordPress网站的服务器上放置了2GB的内存,APC使用了1.2GB。对于Magento安装,请计算64MB,对于带有一些插件的WordPress,请计算40MB。
此外,如果您在同一台服务器上拥有开发网站,请将它们排除在缓存之外。

2
这个!我正在运行WordPress,32M不够用。升级到64M,现在一切都清楚了。检查apc.php吧! - Dave Drager
为了将其增加到64M,您需要添加apc.shm_size=64而不是apc.shm_size=64M(我看到的大多数示例都在末尾添加了M)。在我的apc版本(v3.1.3p1)上无法正常工作。 - Patrick Forget
1
你假定你将拥有大量已经在缓存中保存的文件,这些文件已经比TTL长时间存在于缓存中。c33s提出了一个重要观点。如果所有内容最近被访问过(假设你有70%的缓存一直按照你希望的方式进行访问,并且突然增加了很多额外的不频繁文件),你会因为TTL秒而抛出错误。缓存已满,并且你告诉APC不应清除这些条目,所以它会抱怨。如果TTL为5小时,则需要等待这些不频繁的文件到期的时间为5个小时。 - Matthew Kolb
@MatthewKolb:不应该允许缓存超过APC内存容量的文件。使用过滤器来防止不经常访问的文件被缓存。 - bokan
@Patrick Forget:这取决于情况。在FreeBsd上,当我没有放置M(或G)时,PHP会抱怨。 - Antonello
@PatrickForget - 你正在使用一个非常老的APC版本或者是为另一个发行版编译的版本。所有现代版本的APC都期望在数字后面加上M或G,因为只有兆字节的旧时代已经离我们而去了。 - Fiasco Labs

90

可能与APC有关。

如果您遇到此问题,请指定您的.ini设置,具体来说是您的apc.mmap_file_mask设置。

对于基于文件的mmap,它应该设置为类似以下内容:

apc.mmap_file_mask=/tmp/apc.XXXXXX

要直接从/dev/zero进行内存映射,请使用:

apc.mmap_file_mask=/dev/zero

对于符合POSIX标准的共享内存支持的mmap,请使用:

apc.mmap_file_mask=/apc.shm.XXXXXX

2
我发现这些更改并没有解决问题,因为链接线程上的评论也记录了这一点... - Jonathan Day
3
这个 APC 设置需要更多的信息:http://php.net/apc.configuration#ini.apc.mmap-file-mask - mikeytown2
2
在我的情况下,我不得不从基于文件的更改为符合POSIX标准,以消除错误。 - Attila Fulop
4
我不知道这个答案如何解决问题。当 file_mask 不是这些值之一时,会出现错误吗?如果我有其中一个值并且仍然遇到错误,我需要将其切换为另一个值吗?那么应该是哪一个? - Jeff
我现在感到很害怕,因为我的文件支持的mmap使用的格式是:apc.mmap_file_mask=/tmp/apc.XXXXXX,然后今天我在所有的网站中都遇到了这些错误,所以我需要更改格式为符合POSIX标准的格式,如此处所述:apc.mmap_file_mask=/apc.shm.XXXXXX。我可以保证,唯一有权访问服务器的人是我,而且我没有更改任何与php相关的.ini文件...我现在毫无头绪,对这个问题感到非常困惑,我不希望在睡觉时再次遇到这种情况。我希望有人能够解释一下这个问题,谢谢。 - Christian Tjhai
显示剩余2条评论

36

我的解决方案:

  • apc.ttl=0
  • apc.shm_size=任何您想要的值

编辑开始

警告!

@bokan建议在这里加一个警告。

如果ttl为0,这意味着每个缓存项目可以立即被清除。因此,如果您有一个像2MB这样的小缓存大小和ttl为0,这将使APC变得无用,因为缓存中的数据总是被覆盖。

降低ttl意味着缓存永远不会变满,只有不能替换的项目才能填充它。

因此,您必须在ttl和缓存大小之间选择一个良好的平衡。

在我的情况下,我有一个1GB的缓存大小,所以对我来说足够了。

编辑结束

我在CentOS 5上使用PHP 5.2.17时遇到了同样的问题,发现如果缓存大小很小且ttl参数“高”(如7200),并且有许多要缓存的PHP文件,则缓存会很快填满,并且APC找不到任何可以删除的内容,因为所有文件都仍适合ttl。

增加内存大小只是部分解决方案,如果您的缓存填满了,并且所有文件都在ttl之内,您仍然会遇到此错误。

因此,我的解决方案是将ttl设置为0,以便APC填满缓存,总是有可能清除一些内存以获取新数据。

希望这有所帮助。

编辑: 请参见:http://pecl.php.net/bugs/bug.php?id=16966

下载http://pecl.php.net/get/APC,提取并运行apc.php,您将看到一个漂亮的图表,显示您的缓存使用情况。


2
谢谢,这确实有帮助。我每秒钟大约收到了十几个“无法分配内存”的错误信息。我将缓存大小加倍(从32 MB增加到64 MB),并将ttl降至0。这完全消除了这些错误。 - nicktacular
1
这是我们服务器上的修复。 - Justin
1
这似乎也解决了我的问题。 - anisoptera
1
使用ZWAMP,这似乎也解决了问题。谢谢。 - WernerCD
10
这不是一个解决方案!虽然错误消失了,但APC将几乎被禁用。每次内存满时都会清除所有缓存。请阅读Brideau提供给我们的手册。php.net/manual/en/apc.configuration.php#ini.apc.ttl. - bokan
显示剩余4条评论

7
运行apc.php脚本是理解问题的关键,这对我们正确设置缓存大小和暂时解决问题非常有帮助。

1
正如c33s所说: 下载http://pecl.php.net/get/APC并提取运行apc.php,您将获得一个漂亮的图表,展示您的缓存使用情况。 - bokan

4
如Bokan所提到的,如果有空间,您可以增加内存,并且他正确指出将TTL设置为0是多么的低效。
注意:这是我解决特定问题的方法。这是一个通用问题,可能由很多因素引起,所以只有在您遇到错误并且认为是由重复加载PHP文件导致时,请按照以下步骤操作。
我遇到的问题是当我发布新版本的PHP应用程序时。我用新文件替换了所有的.php文件,APC会将两个版本都加载到缓存中。
因为我没有足够的内存来存储两个版本的php文件,APC会耗尽内存。
有一个选项叫做apc.stat,可以告诉APC检查特定文件是否已更改,如果是,则替换它。这通常适用于开发,因为您不断进行更改,但是在生产中通常关闭,就像在我的情况下一样-http://www.php.net/manual/en/apc.configuration.php#ini.apc.stat 如果您可以接受性能损失,打开apc.stat选项将解决此问题。
我针对我的问题提出的解决方案是检查项目版本是否更改,如果更改,则清空缓存并重新加载页面。
define('PROJECT_VERSION', '0.28'); 

if(apc_exists('MY_APP_VERSION') ){

    if(apc_fetch('MY_APP_VERSION') != PROJECT_VERSION){
        apc_clear_cache();
        apc_store ('MY_APP_VERSION', PROJECT_VERSION);
        header('Location: ' . 'http'.(empty($_SERVER['HTTPS'])?'':'s').'://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']);
        exit;  
    }

}else{
    apc_store ('MY_APP_VERSION', PROJECT_VERSION);
}

4

谢谢,你指出了正确的解决方案。降低TTL就像禁用APC一样。 - bokan

2

这对我们的团队(在同一服务器上运行多个WordPress网站)起作用。

我们在 /etc/php.d/apc.ini 文件中更改了内存设置。它被设置为64M,所以我们将其加倍到128M。

apc.shm_size=128M


1

从互联网上看,可能有各种原因。在我的情况下,除了...

apc.shm_size = 64M

...我已经清除了之前收到的无数警告。


1
在我的系统上,我需要将apc.shm_size = 64M插入到/usr/local/etc/php.ini中(FreeBSD 9.1)。然后,当我查看apc.php时(我从/usr/local/share/doc/APC/apc.php复制到/usr/local/www/apache24/data),我发现缓存大小已经从默认的32M增加到了64M,并且我不再收到大缓存满计数的通知。
参考文献: http://au1.php.net/manual/en/apc.configuration.php 还要阅读Bokan的评论,他们非常有帮助。

1
为解决此问题,请将apc.shm_size的值设置为整数。 找到您的apc.ini文件(在我的系统中,apc.ini文件位于/etc/php5/conf.d/apc.ini),并设置: apc.shm_size = 1000

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