使用分层对象结构时如何保持低内存

Ale*_*lex 6 php memory object

我有一个简单的对象,可以生成相同类型的子项.

这个对象有一个toHTML方法,它做了类似的事情:

$html  = '<div>' . $this->name . '</div>';
$html .= '<ul>';

foreach($this->children as $child)
  $html .= '<li>' . $child->toHTML() . '</li>';

$html .= '</ul>';

return $html;
Run Code Online (Sandbox Code Playgroud)

问题在于,当对象很复杂时,就像很多带孩子的孩子一样,内存使用率会急剧上升.

如果我只是print_r提供这个对象的多维数组我得到1 MB内存使用,但在我将数组转换为我的对象后,print $root->toHtml()它需要10 MB!

我怎样才能解决这个问题?

====================================

制作了一个类似于我的真实代码的简单类(但更小):

class obj{
  protected $name;
  protected $children = array();    
  public function __construct($name){
    $this->name = $name;
  }
  public static function build($name, $array = array()){
    $obj = new self($name);
    if(is_array($array)){
      foreach($array as $k => $v)
        $obj->addChild(self::build($k, $v));
    }  
    return $obj;
  }
  public function addChild(self $child){
    $this->children[] = $child;
  }
  public function toHTML(){
    $html  = '<div>' . $this->name . '</div>';
    $html .= '<ul>';
    foreach($this->children as $child)
      $html .= '<li>' . $child->toHTML() . '</li>';
    $html .= '</ul>';

    return $html;    
  }
}
Run Code Online (Sandbox Code Playgroud)

并测试:

$big = array_fill(0, 500, true);
$big[5] = array_fill(0, 200, $big);

print_r($big);
// memory_get_peak_usage() shows 0.61 MB

$root = obj::build('root', $big);
// memory_get_peak_usage() shows 18.5 MB wtf lol

print $root->toHTML();
// memory_get_peak_usage() shows 24.6 MB
Run Code Online (Sandbox Code Playgroud)

Bab*_*aba 1

介绍

由于您仍将输出 HTML,因此无需间接消耗内存来保存它。

这是一个简单的类:

  • 从多维数组构建菜单
  • 内存高效使用迭代器
  • 可以写入SocketStreamFilearrayIterator

例子

$it = new ListBuilder(new RecursiveArrayIterator($big));

// Use Echo
$m = memory_get_peak_usage();
$it->display();
printf("%0.5fMB\n", (memory_get_peak_usage() - $m) / (1024 * 1024));
Run Code Online (Sandbox Code Playgroud)

输出

0.03674MB
Run Code Online (Sandbox Code Playgroud)

其他输出接口

$big = array_fill(0, 500, true);
$big[5] = array_fill(0, 200, $big);
Run Code Online (Sandbox Code Playgroud)

简单比较

// Use Echo
$m = memory_get_peak_usage();
$it->display();
$responce['echo'] = sprintf("%0.5fMB\n", (memory_get_peak_usage() - $m) / (1024 * 1024));

// Output to Stream or File eg ( Socket or HTML file)
$m = memory_get_peak_usage();
$it->display(fopen("php://output", "w"));
$responce['stream'] = sprintf("%0.5fMB\n", (memory_get_peak_usage() - $m) / (1024 * 1024));

// Output to ArrayIterator
$m = memory_get_peak_usage();
$it->display($array = new ArrayIterator());
$responce['iterator'] = sprintf("%0.5fMB\n", (memory_get_peak_usage() - $m) / (1024 * 1024));

// Output to Array
$m = memory_get_peak_usage();
$it->display($array = []);
$responce['array'] = sprintf("%0.5fMB\n", (memory_get_peak_usage() - $m) / (1024 * 1024));

echo "\n\nResults \n";
echo json_encode($responce, 128);
Run Code Online (Sandbox Code Playgroud)

输出

Results
{
    "echo": "0.03684MB\n",
    "stream": "0.00081MB\n",
    "iterator": "32.04364MB\n",
    "array": "0.00253MB\n"
}
Run Code Online (Sandbox Code Playgroud)

使用类别

class ListBuilder extends RecursiveIteratorIterator {
    protected $pad = "\t";
    protected $o;

    public function beginChildren() {
        $this->output("%s<ul>\n", $this->getPad());
    }

    public function endChildren() {
        $this->output("%s</ul>\n", $this->getPad());
    }

    public function current() {
        $this->output("%s<li>%s</li>\n", $this->getPad(1), parent::current());
        return parent::current();
    }

    public function getPad($n = 0) {
        return str_repeat($this->pad, $this->getDepth() + $n);
    }

    function output() {
        $args = func_get_args();
        $format = array_shift($args);
        $var = vsprintf($format, $args);

        switch (true) {
            case $this->o instanceof ArrayIterator :
                $this->o->append($var);
                break;
            case is_array($this->o) || $this->o instanceof ArrayObject :
                $this->o[] = $var;
                break;
            case is_resource($this->o) && (get_resource_type($this->o) === "file" || get_resource_type($this->o) === "stream") :
                fwrite($this->o, $var);
                break;

            default :
                echo $var;
                break;
        }
    }

    function display($output = null) {
        $this->o = $output;
        $this->output("%s<ul>\n", $this->getPad());
        foreach($this as $v) {
        }
        $this->output("%s</ul>\n", $this->getPad());
    }
}
Run Code Online (Sandbox Code Playgroud)

结论

正如您所看到的,使用迭代器循环速度很快,但在迭代器或对象中存储值可能不那么内存有效。