当前位置 : 主页 > 网络编程 > PHP >

PHP - php7基本变量与内存管理机制

来源:互联网 收集:自由互联 发布时间:2023-09-03
概述 这是源码php7系列的第二篇文章,主要介绍变量的机制和内存的管理,我相信学习源码是对代码整体提升的有效手段,话不多说,开始吧! 变量实现 1. 解密zval zval 底层结构: str

概述

这是源码php7系列的第二篇文章,主要介绍变量的机制和内存的管理,我相信学习源码是对代码整体提升的有效手段,话不多说,开始吧!

变量实现

1. 解密zval

zval 底层结构:

struct_zval_struct {
zend_value value; //8个字节
union u1; //4个字节
union u2; //4个字节
}

对于vue来说是一个联合体,zval一共16个字节,u1 4个字节,u2四个字节,value结构体如下:

typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
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;

虽然PHP属于弱类型语言,但是在底层实现中还是要区分类型的,因为类型里有天然的长度,类型引势内存的长度。

底层做了很多类型转化的处理,让我们不用关心php的类型和长度,这也是php开发高效的原因之一。

变量知识点:

  • value、u1、u2都是联合体,在底层是要区分类型的
  • u2里面有个重要的变量next,next会在数组中解决冲突使用

2.写时复制(Copy On Write)

struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};

zend\_refcounted\_h 作用是string类型的引用计数的结构体,h是字符串对应的hash值,它后面会用到数组里,len代表字符串的长度,char是字符串的值,因为C言语中字符串遇到\0就会自动结束,二进制是不安全的,所以php加上了长度。

$value1 = 'stark';
$value2 = $value1;
$value2 = 'zcc';

php的写时复制是这样发生的,如果把 PHP - php7基本变量与内存管理机制_内存管理value2,两个变量指向的是同一个物理内存地址,存在硬盘上的某一个块里,也许地址是0x7fff5e01c00,当$value2赋值新的值时,zend\_refcounted\_h引用计数减一,zcc存入新的地址。可以看我之前的​​文章​​。

3.字符串的引用类型

struct _zend_reference {
zend_refcounted_h gc;
zval val;
};

可以跟着代码执行一下,看看你心里的预期和实际打印出的值是否一致

$a = 'hello';
$b = &$a;
var_dump($a,$b);

$b = 'stark';
var_dump($a,$b);

unset($b);
var_dump($a,$b);

执行结果:

[root@dd2065d03db8 code]# /usr/local/php7.1.0/bin/php refer.php
string(5) "hello"
string(5) "hello"
string(5) "stark"
string(5) "stark"
string(5) "stark"
NULL

源码中的数组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 consistency)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};

nTableMask是计算数组的索引值,\*arData存储数组里的key=>value的键值对,nNumUsed表示已经使用的空间,nNumOfElements真正的元素个数,nTableSize是arData的大小,nTableSize默认大小是8字节,内存不够每次扩容都x2,以此类推。

内存管理

在malloc申请内存时声明了size大小,但是回收时没有传size,怎么做到准确释放size大小内存的呢?

void *ptr=malloc(size);
free(ptr);

php7内存接口

void *ptr=_emalloc(size);
_efree(ptr);

1.Small内存的管理

内存的基本概念:chunk、page、各种规格的内存。

  • chunk: 2MB 大小的内存
  • page :4KB大小的内存
#define ZEND_MM_CHUNK_SIZE (2 * 1024 * 1024)               /* 2 MB  */
#define ZEND_MM_PAGE_SIZE (4 * 1024) /* 4 KB */
#define ZEND_MM_PAGES (ZEND_MM_CHUNK_SIZE / ZEND_MM_PAGE_SIZE) /* 512 */

内存规格

  • 内存预分配:使用mmap分配chunk

内存分类:

  • 1.Small(30种规格) (size <= 3KB)
  • 2.Large (3KB < size <= 2MB-4KB)
  • 3.Huge(size > 2MB-4KB)

2. Chunk的内存对齐

关于chunk对齐的算法

/**********/
/* Chunks */
/**********/

static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
{
void *ptr = zend_mm_mmap(size);

if (ptr == NULL) {
return NULL;
} else if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) == 0) {
#ifdef MADV_HUGEPAGE
madvise(ptr, size, MADV_HUGEPAGE);
#endif
return ptr;
} else {
size_t offset;

/* chunk has to be aligned */
zend_mm_munmap(ptr, size);
ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
#ifdef _WIN32
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
zend_mm_munmap(ptr, size + alignment - REAL_PAGE_SIZE);
ptr = zend_mm_mmap_fixed((void*)((char*)ptr + (alignment - offset)), size);
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
if (offset != 0) {
zend_mm_munmap(ptr, size);
return NULL;
}
return ptr;
#else
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
if (offset != 0) {
offset = alignment - offset;
zend_mm_munmap(ptr, offset);
ptr = (char*)ptr + offset;
alignment -= offset;
}
if (alignment > REAL_PAGE_SIZE) {
zend_mm_munmap((char*)ptr + size, alignment - REAL_PAGE_SIZE);
}
# ifdef MADV_HUGEPAGE
madvise(ptr, size, MADV_HUGEPAGE);
# endif
#endif
return ptr;
}
}
网友评论