共计 3460 个字符,预计需要花费 9 分钟才能阅读完成。
1、php 垃圾回收与内存泄漏
以下基于官网测试、整理:
php 的 COW(copy-on-write 写时复制)
参考官方:https://www.php.Xdebug 安装与 …
Xdebug 安装与使用
测试环境 php7.3 下
// 调试可见 xdebug_debug_zval('a');
$a = "new string"; //==> a: (refcount=1, is_ref=0)='new string' (length=10)
$c = $b = $a; //==> a: (refcount=1, is_ref=0)string 'new string' (length=10)
unset($b, $c); //==> a: (refcount=1, is_ref=0)='new string' (length=10)
// 复合类型
$a = array('meaning' => 'life', 'number' => 42);
$a['self'] = &$a;
/** ==>
a: (refcount=2, is_ref=0)
array (size=2)
'meaning' => (refcount=1, is_ref=0)string 'life' (length=4)
'number' => (refcount=0, is_ref=0)int 42
'self' => (refcount=2, is_ref=1)
&array<
) **/
class Test{
public $a = 1;
public $b = 'jksj';
function handle(){echo 'hehe';}
}
$test = new Test();
$test2 = new Test();
xdebug_debug_zval('test');
xdebug_debug_zval('test2');
/**
test:
(refcount=1, is_ref=0)
object(Test)[1]
public 'a' => (refcount=0, is_ref=0)int 1
public 'b' => (refcount=1, is_ref=0)string 'jksj' (length=4)
test2:
(refcount=1, is_ref=0)
object(Test)[2]
public 'a' => (refcount=0, is_ref=0)int 1
public 'b' => (refcount=1, is_ref=0)string 'jksj' (length=4)
**/
a. 引用次数
变量容器在”refcount“变成 0 时就被销毁。任何关联到某个变量容器的变量离开它的作用域 (比如:函数执行结束),或者对变量调用了函数 unset() 时,”refcount“就会减 1
b. 内存泄漏
清理变量容器的问题 (Cleanup Problems)
— 尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。
— 如果你要实现分析算法,或者要做其他像一个子元素指向它的父元素这样的事情,这种情况就会经常发生。当然,同样的情况也会发生在对象上,实际上对象更有可能出现这种情况,因为对象总是隐式的被引用。
— 如果上面的情况发生仅仅一两次倒没什么,但是如果出现几千次,甚至几十万次的内存泄漏,这显然是个大问题。这样的问题往往发生在长时间运行的脚本中,比如请求基本上不会结束的守护进程(deamons) 或者单元测试中的大的套件 (sets) 中。后者的例子:在给巨大的 eZ(一个知名的 PHP Library) 组件库的模板组件做单元测试时,就可能会出现问题。有时测试可能需要耗用 2GB 的内存,而测试服务器很可能没有这么大的内存。
c. 回收周期
— 首先,我们先要建立一些基本规则,如果一个引用计数增加,它将继续被使用,当然就不再在垃圾中。如果引用计数减少到零,所在变量容器将被清除 (free)。就是说,仅仅在引用计数减少到非零值时,才会产生垃圾周期(garbage cycle)。
— 其次,在一个垃圾周期中,通过检查引用计数是否减 1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。
— 当垃圾回收机制打开时,每当根缓存区存满时,就会执行上面描述的循环查找算法。
当垃圾回收机制关闭时,如果根缓冲区存满了可能根,更多的可能根显然不会被记录。那些没被记录的可能根,将不会被这个算法来分析处理。如果他们是循环引用周期的一部分,将永不能被清除进而导致内存泄漏。
php7.3 *zval 需要的内存不再是单独从堆上分配,不再自己存储引用计数。数字的引用计数 refcount 为 0,字符串、对象从 1 开始,数组从 2 开始,引用计数由其自身来存储;自身引用型计数属性与原本相同 展示为:&array<。当根缓存区存满时,会执行 gc_collect_cycles()实现自动回收。
官方 After testing, breaking up memory intensive code into a separate function allows the garbage collection to work. 推荐把
内存密集型代码放到函数中,方便内存及时回收。
2、面向对象之 this、self 和 static
参考《PHP 手册 ›语言参考 ›类与对象》
a.self 和 static
class a{
static protected $test="class a";
public function static_test(){
echo self::$test;
echo static::$test;
}
}
class b extends a{static protected $test="class b";}
$obj = new b();
$obj->static_test();
// new a() => a a
// Results class a
// Results class b
当前类无继承时,self 和 static 均指向自己。
有继承时,self 指向 预先 / 事先 静态化的父类,static 完成静态化的自己。ststic 的方式也被称为 – 延迟静态绑定 –。
b. 重载时的情况
class staticparent {
static $parent_only;
static $both_distinct;
function __construct() {
static::$parent_only = 'fromparent';
static::$both_distinct = 'fromparent';
}
}
class staticchild extends staticparent {
static $child_only;
static $both_distinct;
function __construct() {
static::$parent_only = 'fromchild';
static::$both_distinct = 'fromchild';
static::$child_only = 'fromchild';
}
}
$a = new staticparent;
echo 'Parent: parent_only=', staticparent::$parent_only, ', both_distinct=', staticparent::$both_distinct, "<br/>\r\n";
$a = new staticchild;
echo 'Parent: parent_only=', staticparent::$parent_only, ', both_distinct=', staticparent::$both_distinct, "<br/>\r\n";
echo 'Child: parent_only=', staticchild::$parent_only, ', both_distinct=', staticchild::$both_distinct, ', child_only=', staticchild::$child_only, "<br/>\r\n";
//Parent: parent_only=fromparent, both_distinct=fromparent
//Parent: parent_only=fromchild, both_distinct=fromparent
//Child: parent_only=fromchild, both_distinct=fromchild, child_only=fromchild
这也是 延迟静态绑定 运用的一个例子。
3、技巧
参考脚本之家整理的 2019 最新的 php 技巧