【问题标题】:Is pg_free_result() necessary, even if the result goes out of scope?pg_free_result() 是否必要,即使结果超出范围?
【发布时间】:2013-05-09 09:07:41
【问题描述】:

PHP 文档对pg_free_result() 有这样的说法:

只有在脚本期间内存消耗时才需要调用此函数 执行是个问题。否则,所有结果内存将 脚本结束时自动释放。

http://www.php.net/manual/en/function.pg-free-result.php

我会(也许天真地)期望通过调用 pg_query() 返回的资源在超出范围时会被垃圾回收。

在这样的假设函数中:

function selectSomething ()
{
    $res = pg_query("SELECT blah FROM sometable");
    // do something with $res
    pg_free_result($res);   // required or not?
}

真的有必要在最后调用pg_free_result()吗?

换句话说,如果我调用这个函数 1000 次,它会占用内存来存储所有 1000 个结果吗?

编辑:我说的是典型案例,即pg_connect() 而不是pg_pconnect()。

【问题讨论】:

  • 取决于,你是在强制一个新的 pg 连接,还是连接是持久的?如果您使用的是持久连接,the resource might not be GC'ed。但为什么不分析脚本。编写一个小测试用例并检查内存使用情况。如果一直增加直到脚本结束,添加pg_free_result,然后再次运行...
  • 这是一个更普遍的问题,无论连接类型如何。我知道我可以只写一个测试,但是文档是如此模糊,这让我很困扰。一次性测试只能回答我针对一个特定平台和版本(PHP 和/或 libpq)的问题。
  • 实际上,你有一个很好的观点。我已经编辑了问题以声明我正在谈论pg_connect()。

标签: php postgresql memory-leaks


【解决方案1】:

正如 Elias Van Ootegem 正确指出的那样,您几乎肯定会使用持久连接。在查询之后使用持久连接,结果必须在内存中继续,因为您可能希望从中收集更多数据(例如最后一个错误)。

所以这归结为良好的做法。如果您在具有 2M 可用内存的环境中运行,并且您的脚本有时可以达到 0.1M 内存,那么上限是 20 个并发连接调用该脚本。之后,进一步的 Web 请求将排队或丢弃。无需天才就能意识到这对 DDoS 攻击有多么脆弱。

因此,最佳做法是在完成后立即清空内存。这几乎适用于任何编程或脚本。当系统受到压力并且需求很高时,在整个内存范围内可以服务的请求越多越好。如果您可以降低脚本的最大内存占用,则可以增加可以合理尝试调用它的并发连接数,从而增加脚本可以处理的负载。

理想的做事方式是尽快释放资源。没有理由不这样做。

【讨论】:

  • 我在 Elias 的回答中对此发表了评论,但该回答与 cmets 一起被删除。总结一下:不,这绝对不是持久连接。在 pg_query 中不使用 $conn 参数只是隐式使用pg_connect()(或pg_pconnect())打开的最后一个连接。持久的连接是完全不同的东西。除其他外,它们将保持 HTTP 请求之间的锁和事务处于活动状态(如果使用 mod_php),这几乎总是不需要的。我们使用 PgBouncer 作为连接池。
  • 就最佳实践而言,PHP 使用垃圾收集,这通常不需要显式地释放()资源。当它们变得不可访问时(即超出范围时),它们将被标记为垃圾收集。垃圾收集语言的最佳实践是让 GC 做它的事情。问题是这是否也适用于 PG 结果资源,它 确实 具有 free() 函数,并且从我作为对 Joel 答案的评论发布的测试来看,似乎确实如此。但这只是一个测试,没有正式记录的行为。
【解决方案2】:

这是我的测试结果:

(之前/之后)

与:631288 / 631384

没有:631288 / 631640

随意使用此代码自行运行测试:

<?php
class test {
    private static $tests = 5000;

    public function __construct() {
        $dbconn = pg_connect("host=### dbname=### user=### password=###")
            or die('Could not connect: ' . pg_last_error());

        self::test(self::$tests, true);
        self::test(self::$tests, false);
    }

    private function test($times, $with) {
        echo ($with ? "with:<br />\n" : "without:<br />\n") . memory_get_usage() ."<br />\n";

        for($i = 0; $i < $times; $i++) {
            $res = pg_query("SELECT * FROM chowder");

            if($with) {
                pg_free_result($res);
            }
        }

        echo memory_get_usage() ."<br /><br />\n\n";
    }

}

$test = new test();

【讨论】:

  • 我无法为问题中提到的用例复制您的结果。看这里:pastebin.com/ghw1PHuE
  • 顺便说一句,您的测试通过将键和值添加到 $memory_usage 来直接影响内存使用情况。删除这些,无论有无(加上减去几个字节),你都会得到相同的结果。
  • 啊,是的,我认为它会使用数组添加微不足道的数量。我已经更新了我的测试和结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多