🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# Server内存管理机制 [TOC] `Server`启动后内存管理的底层原理与普通php-cli程序一致。具体请参考`Zend VM`内存管理方面的文章。 ## 局部变量 在事件回调函数返回后,所有局部对象和变量会全部回收,不需要`unset`。如果变量是一个资源类型,那么对应的资源也会被PHP底层释放。 ~~~ function test() { $a = new Object; $b = fopen('/data/t.log', 'r+'); $c = new swoole_client(SWOOLE_SYNC); $d = new swoole_client(SWOOLE_SYNC); global $e; $e['client'] = $d; } ~~~ * `$a`,`$b`,`$c`都是局部变量,当此函数`return`时,这`3`个变量会立即释放,对应的内存会立即释放,打开的IO资源文件句柄会立即关闭。 * `$d`也是局部变量,但是`return`前将它保存到了全局变量`$e`,所以不会释放。当执行`unset($e['client'])`时,并且没有任何其他`PHP变量`仍然在引用`$d`变量,那么`$d`就会被释放。 ## 全局变量 在`PHP`中,有`3`类全局变量。 * 使用`global`关键词声明的变量 * 使用`static`关键词声明的类静态变量、函数静态变量 * `PHP`的超全局变量,包括`$_GET`、`$_POST`、`$GLOBALS`等 全局变量和对象,类静态变量,保存在`Server`对象上的变量不会被释放。需要程序员自行处理这些变量和对象的销毁工作。 ~~~ class Test { static $array = array(); static $string = ''; } function onReceive($serv, $fd, $reactorId, $data) { Test::$array[] = $fd; Test::$string .= $data; } ~~~ * 在事件回调函数中需要特别注意非局部变量的`array`类型值,某些操作如`TestClass::$array[] = "string"`可能会造成内存泄漏,严重时可能发生爆内存,必要时应当注意清理大数组。 * 在事件回调函数中,非局部变量的字符串进行拼接操作是必须小心内存泄漏,如`TestClass::$string .= $data`,可能会有内存泄漏,严重时可能发生爆内存。 ## 解决方法 * 同步阻塞并且请求响应式无状态的`Server`程序可以设置`max_request`和`task_max_request`,当`Worker`进程/`Task`进程结束运行时或达到任务上限后进程自动退出。该进程的所有变量/对象/资源均会被释放回收。 * 程序内在`onClose`或设置`定时器`及时使用`unset`清理变量,回收资源 ## 异步客户端 `Swoole`提供的异步客户端与普通的`PHP`变量不同,异步客户端在发起`connect`时底层会增加一次引用计数,在连接`close`时会减少引用计数。 > 包括`swoole_client`、`swoole_mysql`、`swoole_redis`、`swoole_http_client` ~~~ function test() { $client = new swoole_client(SWOOLE_TCP | SWOOLE_ASYNC); $client->on("connect", function($cli) { $cli->send("hello world\n"); }); $client->on("receive", function($cli, $data){ echo "Received: ".$data."\n"; $cli->close(); }); $client->on("error", function($cli){ echo "Connect failed\n"; }); $client->on("close", function($cli){ echo "Connection close\n"; }); $client->connect('127.0.0.1', 9501); return; } ~~~ * `$client`是局部变量,常规情况下return时会销毁。 * 但这个`$client`是异步客户端在执行`connect`时swoole引擎底层会增加一次引用计数,因此return时并不会销毁。 * 该客户端执行`onReceive`回调函数时进行了`close`或者服务器端主动关闭连接触发`onClose`,这时底层会减少引用计数,`$client`才会被销毁。