PHP垃圾收集澄清

Posted

技术标签:

【中文标题】PHP垃圾收集澄清【英文标题】:PHP Garbage Collection clarification 【发布时间】:2011-12-11 08:29:40 【问题描述】:

php 手册中,session.gc_probability 和 session.gc_divisor 声明 gc 将基于此概率发生。我明白了。

我不清楚的是这个概率是逐个会话还是整体的。

因此,如果我发生 GC 的概率是 1% (1/100),这是否意味着如果一个会话不断延长,每次发生 1% 的变化时,该特定会话就会被清理?或者这是否意味着所有现有会话(以及新会话)中的 1% 将触发所有其他现有会话的 GC?

我很确定是后者,我只是想确定一下。

这个问题的目的是在我们的网站上,我希望用户进行长期会话(6 个月)。如果 1% 的会话触发 GC,那么这实际上消除了进行长期会话的目的,因为 GC 最终会每隔一两个小时发生一次。

【问题讨论】:

非常有趣的问题! +1 相关***.com/questions/3865303/… 对于其他阅读本文的尝试上述内容的人,6 个月的会话文件可能会导致严重的性能问题(如下所述)。但是,您可以使用 session_set_save_handler() 编写自定义会话处理程序,该处理程序将使用 DB 而不是 FS,从而消除许多性能损失。 【参考方案1】:

每次执行 PHP 脚本并启动会话时,它都有可能会扫过会话文件夹,从而杀死旧会话。

Cleanup 只会删除在特定时间内未访问的会话。但是 PHP 不保证会话会在这段时间内被销毁。

您的长期会话策略应该可以正常工作,但您可能希望将 1% 降低到 0.1% 左右

需要注意的另一件事是操作系统可能会在重新启动期间清理您的 /tmp 文件夹,因此即使 PHP 不会这样做。

【讨论】:

我已将概率从 1/100 降低到 1/1000000 (0.000001%)。我希望能解决这个问题。此外,这是一个 Magento 站点,因此会话存储在 /var/session 中。据我所知,该文件夹没有被服务器触及(但我猜如果从 Magento 管理员中选择“刷新缓存存储”,它将被删除)。 哦,希望 Magento 不会实现忽略 gc_* 设置的替代会话。 而降低概率的副作用是您将用旧会话填满您的硬盘驱动器。并且会话将花费越来越长的时间来查找和加载,并且在一天结束时,无论如何您仍然很少有机会擦除会话(即使它很小)。您可以查看替代选项,例如编写自己的会话处理程序并准确控制要删除的会话以及何时删除。这并不难。 php.net/manual/en/function.session-set-save-handler.php 我会支持@bumperbox 提到的内容。创建您自己的会话处理程序可以为您提供更大的控制权。 会话数不太可能以某种方式影响速度。我从来没有经历过,Linux 文件系统使用二叉树或类似于按文件名索引的东西,所以它非常快,除非你在那个文件夹中输入“ls”。大小也不应该是问题,除非您将文件存储在会话中。将会话移动到数据库不会直接解决任何这些问题,但是开发人员通常比文件更熟练地使用数据库,因此清理它们可能更容易。【参考方案2】:

上次我查看源每次调用 session_start() “掷骰子”可以这么说,使用除数和概率。如果你点击,那么它将从session.save_path 目录中删除所有早于session.gc_maxlifetime 的文件。我忘记了它是否使用了文件的修改或访问时间,尽管在正常情况下这无关紧要,因为 php 在脚本执行结束时默认覆盖会话文件,因此 mod 和访问时间应该几乎总是非常匹配。

// Rough psuedo code of how php's session_start() function works regarding garbage collection.
function session_start() 
    $percentChanceToGC = 100 * ini_get('session.gc_probability') / ini_get('session.session.gc_divisor');
    $shouldDoGarbageCollection = rand(1, 100) < $percentChanceToGC;
    if ($shouldDoGarbageCollection) 
        $expiredCutoffTime = time() - ini_get('session.gc_maxlifetime');
        foreach (scandir(ini_get('session.save_path')) as $sessionFile) 
            if (filemtime($sessionFile) < $expiredCutoffTime) 
                unlink($sessionFile);
            
        
    

    // ... rest of code ....

如果您希望它们至少存在 6 个月,我不知道您最终会保留多少会话文件。考虑到 php 可能需要一些时间来统计数千个文件以确定它们的年龄。也许考虑其他选项来持久存储这些数据。或者您可以禁用 php gc 并运行一个 cron 作业来删除过时的会话文件。否则,那 1% 的请求会触发 gc 并且必须等待 php;换句话说,它可能会滞后。

【讨论】:

【参考方案3】:

我不是这方面的专家,但通过阅读手册,我会提请您注意另一个设置,session.gc_maxlifetime。来自文档:

session.gc_maxlifetime 指定数据将被视为“垃圾”并可能被清理的秒数。垃圾收集可能在会话开始期间发生(取决于session.gc_probabilitysession.gc_divisor)。

所以如果你把这个设置设置成一个合适的值(60 * 60 * 24 * 365 / 2半年,所以15768000),那么无论其他设置是什么,合适的数据都将不符合垃圾回收的条件。

【讨论】:

我已经将 gc_maxlifetime 设置为 15552000(180 天) - 开发站点上的一切似乎都运行正常,但是一旦我将其推送到现场,它会运行一段时间,然后才开始将用户踢回登录页面。 这个值是指“会话创建后的秒数”还是“上次修改后的秒数”? 它需要结合推迟垃圾收集器,以便您的会话数据将在字典中,同时延长您的 cookie 寿命,以便保存该行会话数据唯一的会话 ID如果我理解正确的话。

以上是关于PHP垃圾收集澄清的主要内容,如果未能解决你的问题,请参考以下文章

PHP中有垃圾收集吗?

PHP 生成器 - 垃圾收集

Java:我是不是需要保护 Thread 对象免受垃圾收集器的影响?

为啥 PHP 的垃圾收集器会降低性能,没有它如何管理内存?

PHP 的垃圾收集机制是怎样的

过早收集资源垃圾