代码之家  ›  专栏  ›  技术社区  ›  Milan BabuÅ¡kov

析构函数vs寄存器关闭函数

  •  11
  • Milan BabuÅ¡kov  · 技术社区  · 16 年前

    我有一个PHP类,它可以动态创建一个PNG图像并将其发送到浏览器。PHP手册说我需要确保 图像破坏 最后调用函数释放内存。现在,如果我不使用类,我将拥有如下代码:

    function shutdown_func() 
    {
        global $img;
        if ($img)
            imagedestroy($img);
    }
    register_shutdown_function("shutdown_func");
    

    不过,我相信我的班级应该打电话给 图像破坏 在类的析构函数中。

    我无法确定析构函数的调用方式是否与shutdown函数相同?例如,如果用户在浏览器中按下停止按钮时执行停止。

    注意:无论你在答案中写了什么,请指向一些支持它的文章或手册页面(URL)。

    4 回复  |  直到 9 年前
        1
  •  12
  •   Milan BabuÅ¡kov    16 年前

    我刚用Apache测试过,PHP被用作Apache模块。我创造了这样一个无尽的循环:

    <?php
    class X
    {
        function __destruct()
        {
            $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
            fputs($fp, "Destroyed\n");
            fclose($fp);
        }
    };
    
    $obj = new X();
    while (true) {
        // do nothing
    }
    ?>
    

    以下是我发现的:

    • 在Firefox中按下停止按钮不会停止此脚本
    • 如果我关闭Apache,就不会调用析构函数
    • 当它达到php max_execution_time时停止,并且不调用destructor

    但是,这样做:

    <?php
    function shutdown_func() {
        $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
        fputs($fp, "Destroyed2\n");
        fclose($fp);
    }
    register_shutdown_function("shutdown_func");
    
    while (true) {
        // do nothing
    }
    ?>
    

    调用shutdown函数。这意味着类destructor不如shutdown函数好。

        2
  •  2
  •   genesis    13 年前

    基于你应该 finish what you start ,我想说析构函数是自由调用的正确位置。

    这个 destructor 将在释放对象时调用,而 shutdown function 在脚本执行完成之前不会调用。正如Wolfie所指出的,如果您强制停止服务器或脚本,则不一定会发生这种情况,但此时,PHP分配的内存将被释放。

    同样由Wolfie指出,当脚本关闭时,PHP将释放脚本资源,因此如果您只是实例化其中一个对象,那么您可能不会注意到巨大的差异。但是,如果您以后确实实例化了这些东西,或者在一个循环中这样做,那么您可能不需要担心内存使用突然激增,因此为了将来的正常性,我返回到最初的建议;将其放入析构函数中。

        3
  •  1
  •   therightstuff    9 年前

    我最近遇到了这个问题,因为我试图专门处理服务器超时的情况下的破坏,我想在错误日志中包含类数据。在引用&$时,我会收到一个错误(尽管我在一些示例中看到过这样做,可能是版本问题或symfony副作用),并且我提出的解决方案相当干净:

    class MyClass
    {
        protected $myVar;
    
        /**
         * constructor, registers shutdown handling
         */
        public function __construct()
        {
            $this->myVar = array();
    
            // workaround: set $self because $this fails
            $self = $this;
            // register for error logging in case of timeout
            $shutdown = function () use (&$self) {
                $self->shutdown();
            };
            register_shutdown_function($shutdown);
        }
    
        /**
         * handle shutdown events
         */
        public function shutdown()
        {
            $error = error_get_last();
            // if shutdown in error
            if ($error['type'] === E_ERROR) {
                // write contents to error log
                error_log('MyClass->myVar on shutdown' . json_encode($this->myVar), 0);
            }
        }
    
        ...
    

    希望这能帮助别人!

        4
  •  0
  •   Henrik Paul    16 年前

    我认为您遗漏的一件大事是,一旦脚本终止,PHP在脚本执行期间分配的所有内存都将被释放。即使用户按下停止按钮,PHP也会处理脚本直到脚本完成,并将其返回给HTTP守护进程以供访问者使用(或不提供,取决于守护进程有多聪明)。

    因此,在脚本执行结束时显式释放内存有点多余。有些人可能会说这是一件好事,但它仍然是多余的。

    但是,在类析构函数的主题上,每当对象被销毁时,都会调用它们,或者由 unset() 或在脚本完成/终止时。

    开发人员的建议是明确释放图像处理中使用的内存,确保绝对不会出现内存泄漏,因为位图可能会在内存方面造成压力(高度*宽度*位深*3(+1,如果您有alpha通道))。

    要满足您的维基百科需求: