首页 > 编程语言 >PHP高级面试题大全(附带详细答案)

PHP高级面试题大全(附带详细答案)

时间:2024-03-24 22:58:05浏览次数:35  
标签:__ 面试题 struct zend zval gc PHP ar 大全

1,zval详解(php5时期的)

/*这个是zval的实际结构,zval就是php中定义变量的容器,你申请一个变量就是创建一个zval
  对于数组,数组本身是一个zval,数组中的每个值也是一个zval
*value;         是值或者是地址,内容是值还是地址,要看type的值是什么
*refcount__gc;  计数,用于垃圾回收,
                $a = 'php'; //变量a的计数是1,不能被回收
                $b = $a; //变量a的计数是2,不能被回收(php是写时复制,变量a,b都是同一个zval)
                unset($b);//变量a的计数是1,不能被回收
                unset($a);//变量a的计数是0,可以被回收,不代表马上就回收,具体要看gc是否触发

*type;          类型,类型有NULL,LONG,DOUBLE,BOOL,ARRAY,OBJECT,STRING,
                IS_RESOURCE等类型
*is_ref__gc;    是否被引用(逻辑值),默认是0(没有引用),$b = &$a;变量a的is_ref__gc=1,被引用
*/
struct _zval_struct {
    zvalue_value value;     
    zend_uint refcount__gc;  
    zend_uchar type; 
    zend_uchar is_ref__gc;
};  

typedef struct _zval_struct zval;  //这个就是把这个结构起个别名

//这是个联合体
typedef union _zvalue_value {
    long lval;                //如果zval中的type是LONG,这个就是long的值      
    double dval;              //如果zval中的type是double,这个就是double的值  
    struct {                  //如果string,是struct的地址,struct里是字符串的地址和长度
        char *val;
        int len;
    } str;
    HashTable *ht;           //如果是array,就是hashtable的地址,因为数组就是hashtable实现的    
    zend_object_value obj;   //如果是object,是对象的结构体
} zvalue_value;

2,zval详解(php7之后的,包括php7版本)

struct _zval_struct {
    zend_value        value; //这个就是下面的那个_zend_value名的联合体
    union {
        struct {
            zend_uchar type,      /*变量类型,null,false,true,long,double,string,array等
                                    null,false,true他们只需要知道类型,不需要访问value*/
            zend_uchar type_flags,/*变量类型掩码,不同的类型会有不同的几种属性。比如当前类型是    
                                  否支持引用计数、是否支持写时复制。主要在内存管理时会用*/
            zend_uchar const_flags,//常量类型标记
            zend_uchar reserved
        } v;
        uint32_t type_info; 
    } u1;
    union {
        uint32_t     var_flags;
        uint32_t     next;             //哈希表中解决哈希冲突时用到
        uint32_t     cache_slot;           
        uint32_t     lineno;             
        uint32_t     num_args;           
        uint32_t     fe_pos;              
        uint32_t     fe_iter_idx;        
    } u2; //一些辅助值
};

typedef struct _zval_struct     zval;  //把这个结构体起个别名zval

//zval的value部分,如果是long,double里面的内容的就是值,否则就是其他类型的地址
typedef union _zend_value {
    zend_long         lval;    //存储的是值
    double            dval;    //存储的是值

    zend_refcounted  *counted; 
    zend_string      *str;     //string字符串,结构的指针
    zend_array       *arr;     //array数组,结构的指针
    zend_object      *obj;     //object对象,结构的指针
    zend_resource    *res;     //resource资源类型,结构的指针
    zend_reference   *ref;     //引用类型,通过&$var_name定义的
    zend_ast_ref     *ast;    
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        uint32_t w1;
        uint32_t w2;
    } ww;
} zend_value;

/*注意:php7的计数都是放到value里的,比如zend_string字符串结构体中,gc就是计数相关的数据
zend_array等结构中也有类似的
*/
struct _zend_string {
        zend_refcounted_h gc;
        zend_ulong        h;            
        size_t            len;
        char              val[1];
};

3,php的写时复制

在写入的时候才真正复制一份,这样做的好处是减少内存的使用。

//以php5时期的zval结构讲解,php7的zval结构不同,原理一样的
$a = 'php';   //创建一变量(结构是一个zval,type是字符串,refcount__gc等于1)
$b = $a;      //创建b变量,底层zval结构还是指向a的zval,这个zval的refcount__gc变成了2
$b = 'php7';  /*写时复制在这个时候发生(改变量b的值,但是变量a的值不变),创建了一个新的zval,值内容 
              是php7,原来那个a对应的zval内容不变,还是php,但是refcount__gc会减1,变成了1
              */

4,php的数组原理(php5的hashtable原理)

php的数组,在底层使用hashtable实现的

//hashtable结构
typedef struct _hashtable {
    uint nTableSize;         //hashtable表的大小,都是2的n次方,扩容都是2倍的扩,最小为8
    uint nTableMask;         //是一个掩码,用于快速计算索引的
    uint nNumOfElements;     //这个数组放入了多少元素,count($ar)返回的就是这个值
    ulong nNextFreeElement;  //下一个可用的数字索引,例如$ar[] = 'abc',索引就是这个确定的
    Bucket *pInternalPointer;//一个指针,在使用current,next,key,end等函数就是这个这个记录位置
    Bucket *pListHead;       //链表的头元素地址
    Bucket *pListTail;       //链表的尾元素地址
    Bucket **arBuckets;      //bucket *类型的数组,bucket是实际存储数据的容器,下面那个结构就是
    dtor_func_t pDestructor;
    zend_bool persistent;
    unsigned char nApplyCount;
    zend_bool bApplyProtection;
} HashTable;

/*这个结构是hashtable的的主要结构,存储数据的容器,数组有多少个元素,就会有多少个Bucket
 *对于数字型索引,直接使用h作为hash值,同时,arKey=NULL 且nKeyLength=0
 *对于字符串索引,arKey保存字符串key, nKeyLength保存该key的长度,h则是key的hash值
*/
typedef struct bucket {
    ulong h;           //数字索引值或者字符串的hash值
    uint nKeyLength;   //key的长度
    void *pData;
    void *pDataPtr;
    struct bucket *pListNext;  //整个hash表的,下一个元素(保证数据顺序)
    struct bucket *pListLast;  //整个hash表的,前一个元素(保证数据顺序)
    struct bucket *pNext;      //相同slot的链表的,下一个元素(hash冲突的时候,寻找数据)
    struct bucket *pLast;      //相同slot的链表的,前一个元素(hash冲突的时候,寻找数据)
    const char *arKey;         //key值
} Bucket;

5,php的数组原理(php7的hashtable原理)

typedef struct _Bucket {
    zval        val;  //值放到了zval中
    zend_ulong  h;   //hash值
    zend_string *key;//key值
} Bucket;
 
typedef struct _zend_array HashTable;
 
struct _zend_array {
    zend_refcounted_h gc;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                    zend_uchar    flags,
                    zend_uchar    nApplyCount,
                    zend_uchar    nIteratorsCount,
                    zend_uchar    reserve)
        } v;
        uint32_t flags;
    } u;
    uint32_t nTableMask;      //哈希值掩码,等于nTableSize的负值(nTableMask = ~nTableSize + 1)
    Bucket *arData;           //存储元素数组,指向第一个Bucket,数组中每个元素都是Bucket
    uint32_t  nNumUsed;       //arData数组已经使用的数量,已用Bucket数,unset元素时,这个不变
    uint32_t  nNumOfElements; //哈希表已有元素数,unset元素时,这个会减1
    uint32_t  nTableSize;     //哈希表总大小,为2的n次方,最小值为8
    uint32_t  nInternalPointer;//内部的指针,用于HashTable遍历
    zend_long nNextFreeElement; //下一个可用的数值索引,如:arr[] = 1
    dtor_func_t pDestructor; // 析构函数
}

6,hashtable扩容原理

当哈希表中存储的键值对超过负载因子阈值时,就需要进行扩容操作。负载因子是指哈希表中当前存储的键值对数量与哈希表总容量之间的比值,默认的负载因子大小为0.75。扩容的目的是减少hash冲突的概率,提高访问和更新效率。

7,哈希冲突的解决方法

链接法:php的hashtable用的是这种方法,如果遇到hash冲突的时候,通过链表把hash冲突的数据弄成一个链表,查找的时候,定位到所在的slot后,遍历这个链表。
开放寻址法:在存入的时候,通过公式 f(key,n)取hash,其中n可以认为是[0,1,2,3....],f(key,0)的hash值寻找slot位,如果没有数据,就使用这个位置,如果有数据,在通过f(key,1)取hash计算slot位,直到找到位置为止。查询的时候也是一样,通过f(key,0)的hash找到slot位,如果有数据,比较key是否相等,相等就是要找的数据,如果不相等,在通过f(key,1)的hash找slot位,如果找到的slot为空,就是没有找到这个key对应的值,可以结束寻找。

8,php的垃圾回收机制(gc)

php.ini配置里的zend.enable_gc是控制gc是否打开,默认打开;

函数gc_enable() 和 gc_disable()在运行时来打开和关闭垃圾回收机制;gc_collect_cycles()强制收集所有现存的垃圾循环周期

每个内存对象(就是zval)都分配一个计数器,当内存对象被变量引用时,计数器+1;
当变量引用撤掉后(执行unset()后),计数器-1;
当计数器=0,且没有引用时,表明内存对象没有被使用,该内存对象可以进行销毁。

垃圾回收的目的不用程序员手动管理内存,提高了编程的安全性和效率,避免了内存溢出的发生。

是如果遇到循环引用,就会出现内存泄漏的情况。

9,php怎么处理循环引用的问题?

循环引用的这种情况,是在php5.3以后才有的解决方法。

//循环引用的过程(对象,数组才会有循环引用的情况)
$ar = [];           //会产生一个zval,类型是数组,计数(refcount__gc)是1
$ar['a'] = 'php';   //也会产生一个zval,类型是字符串,他的计数(refcount__gc)是1
$ar['b'] = &$ar;    /*也会产生一个zval,是一个引用类型,计数(refcount__gc)是1,
                      由于引用了$ar,$ar的计数(refcount__gc)变成了2,zval引用(is_ref__gc)是1,
                     */
unset($ar);         /*会把$ar的计数(refcount__gc)减1,次数(refcount__gc)计数是1,垃圾回收不会回    
                      收$ar
                      unset($ar)的本意是不用这个数组了,可以回收的,但是有循环引用,gc无法回收        
                      他,这就是一个内存泄漏                   
                    */

//----------------分割线------------------

//一个不存在循环引用的过程
$ar = [];           //会产生一个zval,类型是数组,计数(refcount__gc)是1
$ar['a'] = 'php';   //也会产生一个zval,类型是字符串,他的计数(refcount__gc)是1
$ar['b'] = 123;     //也会产生一个zval,计数(refcount__gc)是1,
unset($ar);         //会把$ar的计数(refcount__gc)减1,计数变成了0,直接回收                
                   

解决方法就是增加了一个缓冲区(默认长度是10000),unset某个变量后,如果计数(refcount__gc)大于0(如果等于0,就可以直接回收,肯定是垃圾),那他可能就是存在循环引用的情况,就把它放入这个缓冲区(肯定也是需要排重的),如果这个缓冲区满了,就开始进行判断。判断方式是遍历取出缓冲区的每个元素,然后遍历每个元素,对每个元素做unset模拟操作,这个元素模拟操作后,在回来查看refcount_gc,如果变成了0,就是垃圾,可以回收,否则就不是,不能进行回收,且需要把模拟的unset给反向加回去。


//遇到循环引用,gc是怎么处理的
$ar = [];         
$ar['a'] = 'php'; 
$ar['b'] = &$ar;
unset($ar);       /*unset($ar)后,$ar对应的zval的refcount__gc=1,大于0,可能存在循环引用,需要加        
                  入缓冲区
                  当缓冲区满了,取出$ar,然后遍历$ar的所有元素,模拟操作,    
                  unset($ar['a']),unset($ar['b'])(这步就是断开引用关系,会使$ar的 
                  refcount__gc减1),遍历完了后,在看$ar的refcount__gc已经变成0了,说明是循环引 
                  用,可以进行回收了

                  如果最后发现refcount__gc任然大于0,就不是循环引用,把原来所有元素refcount__gc 
                  的给还原回来
                  */
  

 ------------------------------------------推荐阅读----------------------------------------------------------------

PHP基础面试题大全(附带详细答案)

http,tcp,nginx相关的面试题

标签:__,面试题,struct,zend,zval,gc,PHP,ar,大全
From: https://blog.csdn.net/geegtb/article/details/136762530

相关文章

  • PHP代码审计
    杂phps,可能可查看该php文件源码index.php.bak:index.php文件备份名php7.1+:类型不敏感,反序列化public属性可以直接赋给private_GET$_GET看成一个键值对数组(关联数组)$_GET==array(‘id’=>1,‘name’=>‘xiao’)函数引用&可以修改_GET....的值,不能修改_Request的值在url传......
  • macbook使用php的fastadmin框架验证码不显示解决办法
    macbook使用php的fastadmin框架验证码不显示解决办法给php安装freetype插件freetype下载链接解压插件进入命令行工具,然后进到刚才解压的文件目录中然后执行该命令./configure--enable-static--enable-shared(没有指定prefix,.h文件默认安装到/usr/local/includ......
  • macbook(M1芯片)搭建php+nginx运行环境
    macbook(M1芯片)搭建php+nginx运行环境php安装phpbrewinstallphp//低版本php需要这样安装brewinstallshivammathur/php/[email protected]配置环境变量(低版本的php才需要)echo'exportPATH="/usr/local/opt/[email protected]/bin:$PATH"'>>~/.zshrcecho'exportPATH=&quo......
  • PhpStrom启动报错, java.net.BindException: Address already in use: bind
    问题描述:今天启动phpstromIDE时,突然报错,报错信息如下图:问题分析1.不正确关闭应用(强制关闭):可能是之前启动了一个本地web服务占了端口,在没有停掉服务,直接关闭IDE导致的(尝试了重启电脑也没解决)2.其他应用占用端口:安装了Hyper-V导致端口被占用?显然我的是第一种情况问题解决......
  • 金三银四面试题(二):数据库缓存的数据一致性
    这也是一道非常经典的面试题。可以查到它在很多面经中都出现过。还有一个比较的具体问法其实是:如何保证MySQL和Redis的数据一致性?什么是数据一致性例如将Redis用作MySQL数据的缓存时,由于数据在MySQL中的变更,导致Redis中的缓存数据与实际数据不一致的情况。这种不一致可能......
  • Java面试题:用Java并发工具类,实现一个线程安全的单例模式;使用Java并发工具包和并发框架
    面试题一:设计一个Java并发工具类,实现一个线程安全的单例模式,并说明其工作原理。题目描述:请设计一个Java并发工具类,实现一个线程安全的单例模式。要求使用Java内存模型、原子操作、以及Java并发工具包中的相关工具。考察重点:对Java内存模型的理解。对Java并发工具包的了......
  • mysql中的数据类型大全纯干货------------时间日期类型
    简介(类型预览):在mysql8.0版本中支持的类型主要有:YEAR类型表示年DATE类型表示年,月,日TIME类型表示时,分,秒DATETIME类型表示年,月,日,时,分,秒TIMESTAMP类型通常表示带时区的年,月,日,时,分,秒数据类型单位占用字节格式下限上限YEAR年1YYY或YY19012155TIME时间3H......
  • 在线客服系统php网站源码 支持消息预知
    新增消息预知,消息撤回,消息已读未读,修复需要刷新才能收到消息修复客户来源地址修复消息提示音修复桌面推送提醒下载地址:消息预知在线客服系统php网站源码-麦田吧要求服务器环境:宝塔面板,Nginx1.16-1.18,7.2.23<php<7.3,Mysql5.6-Mysql5.7站点设置点击伪静态选择thin......
  • 2024年C语言最新经典面试题汇总(1-10)
    C语言文章更新目录C语言学习资源汇总,史上最全面总结,没有之一C/C++学习资源(百度云盘链接)计算机二级资料(过级专用)C语言学习路线(从入门到实战)编写C语言程序的7个步骤和编程机制C语言基础-第一个C程序C语言基础-简单程序分析VS2019编写简单的C程序示例简单示例,VS2019调......
  • 【漏洞复现】福建科立迅通信指挥调度平台pwd_update.php SQL注入漏洞 (CVE-2024-2621)
        免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。          ......