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完成静态化的自己。static的方式也被称为 延迟静态绑定 。
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, "
\r\n";
$a = new staticchild;
echo 'Parent: parent_only=', staticparent::$parent_only, ', both_distinct=', staticparent::$both_distinct, "
\r\n";
echo 'Child: parent_only=', staticchild::$parent_only, ', both_distinct=', staticchild::$both_distinct, ', child_only=', staticchild::$child_only, "
\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技巧