php理论点

75次阅读

共计 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 技巧

正文完
 0