基础知识
每个类型的字节长度(x86-64体系)
int 4字节 char 1字节 long 8字节 pointer 8字节
字节对齐
struct test{
char a;
int b;
long c;
void *d;
int e;
char *f;
};
如果单独把每个类型的字节数相加是33字节,但是实际上要使用40字节 内存对齐的本质是cpu对程序内存数据获取的要求,这样可以加快数据IO的过程,避免不必要的计算工作。
联合
uniontest{
char a;
int b;
long c;
};
联合体只共享内存,只使用最大的成员的内存大小 例如:定义一个如下c文件
union data {
u_int32_t a;
u_int32_t b;
u_int32_t c;
} d;
int main() {
d.a = 30;
printf("a %d b %d c %d\n", d.a, d.b, d.c);
d.b = 10;
printf("a %d b %d c %d\n", d.a, d.b, d.c);
}
编译执行后得到的结果是,
$ gcc union.c
$ ./a.out
a 30 b 30 c 30
a 10 b 10 c 10
有了基本认知我们再来看看zval的设计
PHP5的zval设计
PHP5中_zval_struct(zval)结构
struct _zval_struct{
zvalue_value value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
};
typedef union_zvalue _value {
long lval;
double dval;
struct{
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
zend_ast *ast;
} zvalue_value;
通过联合结构很容易看出来,php的整形值实际上是长整型,浮点数是double,字符串是一个结构体,表示byte数组和长度;剩下部分就是数组和对象; zval结构所表示的是一个zvalue_value和引用计数,以及变量类型和是否引用类型等。
使用zend内存池后zval结构的变化
typedef struct _zend_mm_block_info {
size_t _size;
size_t _prev;
} zend_mm_block_info;
typedef struct _zend_mm_block {
zend_mm_block_infoinfo;
} zend_mm_block;
最终一个变量在PHP5中实际占用的内存大小为48字节 这48字节的大小其实有很多的浪费,而这点PHP开发者在PHP7中做了重点优化。
- 题外话:PHP5.3解决循环引用
通过重写分配zval的宏,对zval进行扩充
#undef ALLOC_ZVAL
#define ALLOC_ZVAL(z)\
do{ \
(z)=(zval*)emalloc(sizeof(zval_gc_info));\
GC_ZVAL_INIT(z);
} while(0)
typedef struct _zval_gc_info{
zval z;
union {
gc_root_buffer* buffered;
struct _zval_gc_info *next;
} u;
} zval_gc_info;
从结构体看,猜测的使用的方法是通过对zval结构循环记录来判断是否变量存在循环引用,相当于对有环链表的循环查找。
PHP7的Zval设计
struct _zval_struct {
zend_value value;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type,
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved)
} v;
uint32_t type_info;
} u1;
union {
uint32_t next;
uint32_t cache_slot;
uint32_t lineno;
uint32_t num_args;
uint32_t fe_pos;
uint32_t fe_iter_idx;
uint32_t access_flags;
uint32_t property_guard;
} u2;
};
typedef union _zend_value {
zend_long lval;
double dval;
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
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;
zval结构的主要变化时多了两个union结构,我们主要看下这个变化。
u1字段的含义
- type:记录变量类型。
- type_flag: 对应变量类型特有的标记,不同类型的变量对应的flag也不同
IS_TYPE_CONSTANT
IS_TYPE_IMMUTABLE
IS_TYPE_REFCOUNTED
IS_TYPE_COLLECTABLE
IS_TYPE_COPYABLE
- const_flag:常量类型的标记,对应的属性有:
#define IS_CONSTANT_UNQUALIFIED 0x010
#define IS_CONSTANT_VISITED_MARK 0x020
#define IS_CONSTANT_CLASS0 x080
#define IS_CONSTANT_IN_NAMESPACE 0x100
u2中的字段信息
- next:用来解决哈希冲突问题,记录冲突的下一个元素位置
- cache_slot:运行时缓存。在执行函数时会优先去缓存中查找,若缓存中没有,会在全局的function表中查找。
- lineno:文件执行的行号,应用在AST节点上
- num_args:函数调用时传入参数的个数
- fe_pos:遍历数组时的当前位置
- fe_iter_idx:跟fe_pos用途类似,只是这个字段是针对对象的。
- access_flags:对象类的访问标志,常用的标识有public、protected、private。
- property_guard:防止类中魔术方法的循环调用,
PHP7的zval的内存占用情况
对比PHP5的内存占用48个字节,PHP7的zval内存占用要小很多。
参考: https://book.douban.com/subject/30455287/
|