欢迎光临
我们一直在努力

php方案 PHP的逃逸分析

先说概念:

逃逸 = 变量活过了它被创建的那个函数
不逃逸 = 变量在函数结束时就死了

逃逸的变量 GC 要追踪更久,内存压力更大
不逃逸的可以提前回收

composer require nikic/phpparser

<?php
require 'vendor/autoload.php';

use PhpParser\\NodeFinder;
use PhpParser\\ParserFactory;
use PhpParser\\Node;

$code = <<<'PHP'
<?php
function process() {
$local = new stdClass();
$escaped = new stdClass();
$fn = function() use ($local) { return $local; };
return $escaped;
}
PHP;

$ast = (new ParserFactory)->createForNewestSupportedVersion()->parse($code);
$finder = new NodeFinder;

foreach ($finder->findInstanceOf($ast, Node\\Stmt\\Return_::class) as $ret) {
if ($ret->expr instanceof Node\\Expr\\Variable) {
echo "return 逃逸: \\${$ret->expr->name}\\n";
}
}

foreach ($finder->findInstanceOf($ast, Node\\Expr\\ClosureUse::class) as $use) {
echo "闭包捕获逃逸: \\${$use->var->name}\\n";
}

输出:
return 逃逸: $escaped
闭包捕获逃逸: $local


逐行解释:

$code = <<<'PHP' PHP;
把要分析的 PHP 代码当字符串传进来,这里故意写了两种逃逸:return 带出去、闭包捕获。


$ast = (new ParserFactory)->createForNewestSupportedVersion()->parse($code);
把代码字符串解析成 AST(抽象语法树),就是把代码变成一棵可以遍历的树形结构,每个节点代表一个语法元素。


$finder = new NodeFinder;
AST 遍历工具,用来在树里找特定类型的节点,不用自己写递归。


$finder->findInstanceOf($ast, Node\\Stmt\\Return_::class)
在整棵树里找所有 return 语句节点,返回一个数组。


if ($ret->expr instanceof Node\\Expr\\Variable) {
echo "return 逃逸: \\${$ret->expr->name}\\n";
}
检查 return 的是不是一个变量(不是字面量、不是表达式),是的话就说明这个变量逃逸了,打出变量名。


$finder->findInstanceOf($ast, Node\\Expr\\ClosureUse::class)
找所有闭包的 use() 捕获列表,use ($local) 里的 $local 就是一个 ClosureUse 节点。


echo "闭包捕获逃逸: \\${$use->var->name}\\n";
被闭包捕获的变量也算逃逸,因为闭包可能活得比原函数更久,变量不能提前释放。


两种逃逸对比:

$escapedreturn 带出函数 → 调用方拿着引用,GC 不能回收
$local → 闭包 use 捕获 → 闭包活着一天,$local 就活着一天
$nothing → 函数内用完就扔 → 函数返回时立即可回收,最理想

赞(0)
未经允许不得转载:171主机测评 » php方案 PHP的逃逸分析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址