zoukankan      html  css  js  c++  java
  • 关于ThinkPHP执行长时间任务可能导致PHP使用内存越来越大的问题

    ThinkPHP执行长时间任务时,可能导致PHP使用内存越来越大,最后因为内存超出配置限额而程序挂掉。  

    其实这在很久以前就无意之中发现的一个问题。

    3.x之前有这个问题,5.0以后的,应该是已修复了的(我看了下5.0.6)。

    这个问题,一句话说明,是因为ThinkPHP在记录系统日志的时候出现的问题(ThinkPHP在运行过程中,会记录自己的运行日志,根据是否开启DEBUG模式,记录的日志信息有不同,不多说)。

    因为一个进程执行完,会在很多地方打点记录日志,但是为了提高写日志文件的效率,ThinkPHP先是给所有日志信息记录在内存里(一个数组),最后一次性写入文件。正是因为这个科学的做法,导致了可能出现的不科学的结果,那就是,在长时间执行循环任务时,尤其是会循环操作数据库时而又开启了DEBUG模式的情况下(日志信息会记录数据库语句等,信息较多),就会有大量的日志信息,越来越多的存入这个数组里,造成内存占用越来越大,最终可能导致程序奔溃。

    看代码:

        /**
    * 文件:ThinkPHPLibraryThinkLog.class.php * 记录日志 并且会过滤未经设置的级别 * @static * @access public * @param string $message 日志信息 * @param string $level 日志级别 * @param boolean $record 是否强制记录 * @return void
    */ static function record($message,$level=self::ERR,$record=false) { if($record || false !== strpos(C('LOG_LEVEL'),$level)) { self::$log[] = "{$level}: {$message} "; } }

    如果遇到这种情况,修改一下此方法即可。这里提供一个最简单的修改方法(在未开启DEBUG模式的情况下,直接不记录日志):

        static function record($message,$level=self::ERR,$record=false) {
            if( !APP_DEBUG ) return false; //在debug为开启情况下,不记录日志 by ztg  
            if($record || false !== strpos(C('LOG_LEVEL'),$level)) {
                self::$log[] =   "{$level}: {$message}
    ";
            }
        }

    这样,当执行长时间的循环任务时,给DEBUG关闭后,则无日志记录,可不会再出现这种情况了。

    另外,附带ThinkPHP5.0后的代码,可以看出,在保存日志的机制上,已经完全变了,会循环的定量保存,然后清空内容,避免这种情况:

        /**
         * 记录调试信息
         * @param mixed  $msg  调试信息
         * @param string $type 信息类型
         * @return void
         */
        public static function record($msg, $type = 'log')
        {
            self::$log[$type][] = $msg;
            if (IS_CLI && count(self::$log[$type]) > 100) {
                // 命令行下面日志写入改进
                self::save();
            }
        }
    
    
    
        /**
         * 保存调试信息
         * @return bool
         */
        public static function save()
        {
            if (!empty(self::$log)) {
                if (is_null(self::$driver)) {
                    self::init(Config::get('log'));
                }
    
                if (!self::check(self::$config)) {
                    // 检测日志写入权限
                    return false;
                }
    
                if (empty(self::$config['level'])) {
                    // 获取全部日志
                    $log = self::$log;
                    if (!App::$debug && isset($log['debug'])) {
                        unset($log['debug']);
                    }
                } else {
                    // 记录允许级别
                    $log = [];
                    foreach (self::$config['level'] as $level) {
                        if (isset(self::$log[$level])) {
                            $log[$level] = self::$log[$level];
                        }
                    }
                }
    
                $result = self::$driver->save($log);
                if ($result) {
                    self::$log = [];
                }
    
                return $result;
            }
            return true;
        }
  • 相关阅读:
    双缓冲法解决重绘和闪屏问题
    VC设置视图背景颜色方法
    C++ map,set内部数据结构
    红黑树、平衡二叉树
    堆和栈的区别
    C/C++查找一定范围内的素数(筛法)
    动态内存的传递
    TCP三次握手连接
    php分享十五:php的数据库操作
    php分享十四:php接口编写
  • 原文地址:https://www.cnblogs.com/whatmiss/p/7864983.html
Copyright © 2011-2022 走看看