【问题标题】:PHP - why does passing by reference use the same amount of memory?PHP - 为什么通过引用传递使用相同数量的内存?
【发布时间】:2020-05-09 05:51:03
【问题描述】:

我的 PHP 脚本出现“内存耗尽”。我不知道为什么。限制为 128Mb。我正在从 javascript xmlrequest() 发送一个最大为 10MB(通常小于 1MB)的 json 字符串。 PHP 脚本本身是 43K。即使将所有内容复制十几次,我也不应该耗尽内存。我在 getParentFolders() 对数据库进行了几次调用,但只产生了几行。当然不是它声称我正在使用的 62 兆字节。我使用了 Xdebug(见图),但这并没有告诉我任何有用的信息,只是,是的,我使用了很多内存。

因此,在这一点上,我正在尝试执行“最佳实践”以最大程度地减少内存使用量。一个简单的解决方法,或者我认为,是通过引用传递值。所以我在每个函数的每个变量之前都放了一个“&”。令我惊讶的是,内存消耗没有变化。事实上,它稍微差了几个字节。我也尝试过将每个变量都用作全局变量,但再次令我惊讶的是,差别不大。

那么发生了什么?为什么通过引用传递和使用全局变量不会产生我预期的性能优势? (见图片)

Xdebug '传值'

Xdebug '通过引用'

请注意,它们几乎完全相同。

对于那些想要代码的人,这里是 getParentFolders() 函数,它只返回一个短字符串,但不知何故使用了 70Mb!

function getParentFolders(&$node) {  //returns a string of folders
    debugprint("GetParentFOlders()");  //prints if $DEBUG flag is on
    $parent = getParent($node);
    $path = "";
    while ($parent) {  //goes till we hit root folder
        $path = $parent->title . '/' . $path;  //prepend it
        $parent = getParent($parent);
    }
    return $path;
}

function getParent(&$node) {  //return node that is the parent 
    global $email;
    $parentId = $node->parentId;
    $clientId = $node->parentClient;
    $idCol = $clientId . "_id";
    $tablename = $email . "_bookmarks";
    $query = "SELECT * FROM `$tablename`
                        WHERE $idCol = '$parentId'";  //only return one row since id is unique
    $result = sendquery($query);
    return (object) $result[0];
}

编辑:

澄清一下,我正在寻找关于 PHP 内存使用和最佳实践的技术解释 - 特别是为什么我没有看到内存差异 - 这不是解决问题的方法。

【问题讨论】:

  • PHP 和大多数语言都使用垃圾回收。函数完成时不能保证释放权利。是什么让您认为字符串是问题所在?问题可能出在 sendquery 中的某个地方,或者可能是 getParent 被调用了多少次。
  • 可能是数据中的循环引用导致代码中的无限循环。你的根节点有自己的父节点吗?

标签: php memory memory-management out-of-memory pass-by-reference


【解决方案1】:

使用LIMIT 1确保查询结果返回一行;如果没有找到结果,则使getParent() 返回false 或null,以便可以将其设置为$parent 以退出while 循环。而且我认为您不需要在您的情况下通过引用传递 $node 参数。

【讨论】:

    【解决方案2】:

    $node 的值是一个引用,即指向一个对象的指针。在 PHP 5+ 中,“对象”不是直接的值。您总是通过指向对象的指针来处理对象:当您执行new ClassName 时,它的计算结果为指向对象的指针;当您使用-> 运算符时,它需要一个指向左侧对象的指针。

    所以如果这两个函数都是按值传递的,那么传递的东西的大小就是指针的大小。当您通过引用传递时,这基本上将指针包装在另一层指针中,该指针大小相同(尽管总内存更大,因为有两个指针)。通过引用传递的唯一一点是它允许您在函数内部分配给$node以指向不同的对象或持有不同类型的值,并在调用函数中反映在传递的变量中,但是您不会在此处的任何位置分配给$node(修改指向$node 的对象的字段与分配给$node 不同),因此在这里传递引用是没有意义的。

    (另外,即使你传递了一个值类型,比如 PHP 中的数组,它们也具有写时复制语义,并且在你写它之前不会被复制。即使你写它并导致它一个副本,该副本只会持续到这个局部变量的生命周期,它在函数的末尾结束,所以它不会产生持久的内存使用。)

    【讨论】:

    • 你是说它总是通过引用传递,无论如何?
    • @johnktejik:不,这是没有&的按值传递
    • 嗯...你说对象总是通过引用传递,即指针。
    • @johnktejik:不,我说“对象”没有被传递,因为“对象”不是 PHP5 中的值。当您通过new SomeClass() 创建对象时,结果的类型是引用,即指向对象的指针。如果您将该指针按值传递给一个对象,您将有两个指向同一个对象的指针,就像您将一个持有指向一个对象的指针的变量分配给另一个变量一样。
    【解决方案3】:

    答案是对象不能按值传递。它们总是通过引用传递。 “按引用传递”运算符“&”只会将对象引用(即指针)包装在包装器中,然后传递该新指针。

    关于内存使用,除了数据库查询结果似乎会带来很多开销之外,没有简单的答案,并且您希望自己的“足迹”(即从数据库返回的任何结果或任何变量)尽可能小。

    【讨论】:

      猜你喜欢
      • 2016-03-31
      • 2012-06-24
      • 1970-01-01
      • 2020-10-09
      • 2019-05-10
      • 2016-12-09
      • 2015-12-29
      • 2013-08-22
      • 1970-01-01
      相关资源
      最近更新 更多